zoukankan      html  css  js  c++  java
  • [NOI2018] 你的名字(SAM+主席树)

    Description

    实力强大的小 A 被选为了 ION2018 的出题人,现在他需要解决题目的命名问题。

    小 A 被选为了 ION2018 的出题人,他精心准备了一道质量十分高的题目,且已经把除了题目命名以外的工作都做好了。

    由于 ION 已经举办了很多届,所以在题目命名上也是有规定的,ION 命题手册规定:每年由命题委员会规定一个小写字母字符串,我们称之为那一年的命名串,要求每道题的名字必须是那一年的命名串的一个非空连续子串,且不能和前一年的任何一道题目的名字相同

    由于一些特殊的原因,小 A 不知道 ION2017 每道题的名字,但是他通过一些特殊手段得到了 ION2017 的命名串,现在小 A 有 \(Q\) 次询问:每次给定 ION2017 的命名串和 ION2018 的命名串,求有几种题目的命名,使得这个名字一定满足命题委员会的规定,即是 ION2018 的命名串的一个非空连续子串且一定不会和 ION2017 的任何一道题目的名字相同。

    由于一些特殊原因,所有询问给出的 ION2017 的命名串都是某个串的连续子串,详细可见输入格式。

    【输入格式】
    第二行一个正整数 \(Q\),表示询问次数。

    接下来 \(Q\) 行,每行有一个字符串 \(T\) 和两个正整数 \(l, r\),表示询问如果 ION2017 的命名串是 \(S[l..r]\),ION2018 的命名串是 \(T\) 的话,有几种命名方式一定满足规定。

    保证输入中给出的字符串都是由小写字母构成的。

    【输出格式】
    输出 \(Q\) 行,第 \(i\) 行一个非负整数表示第 \(i\) 个询问的答案。

    【输入样例】

    scbamgepe
    3
    smape 2 7
    sbape 3 8
    sgepe 1 9
    

    【输出样例】

    12
    10
    4
    

    【数据范围与约定】

    测试点 \(\lvert S \rvert \leq\) \(Q \leq\) \(\sum \lvert T \rvert \leq\) 询问限制 其他限制
    1 \(200\) \(200\) \(40000\) 对于所有询问有 \(l = 1, r = \lvert S \rvert\) \(\lvert T \rvert \leq 200\)
    2 \(1000\) \(200\) \(40000\) 对于所有询问有 \(l = 1, r = \lvert S \rvert\) \(\lvert T \rvert \leq 200\)
    3 \(1000\) \(200\) \(40000\) 对于所有询问有 \(l = 1, r = \lvert S \rvert\) \(\lvert T \rvert \leq 200\)
    4 \(1000\) \(200\) \(5 \times 10^5\) 对于所有询问有 \(l = 1, r = \lvert S \rvert\)
    5 \(1000\) \(200\) \(5 \times 10^5\) 对于所有询问有 \(l = 1, r = \lvert S \rvert\)
    6 \(5 \times 10^5\) \(1\) \(5 \times 10^5\) 对于所有询问有 \(l = 1, r = \lvert S \rvert\)
    7 \(5 \times 10^5\) \(1\) \(5 \times 10^5\) 对于所有询问有 \(l = 1, r = \lvert S \rvert\)
    8 \(10^5\) \(10^5\) \(2 \times 10^5\) 对于所有询问有 \(l = 1, r = \lvert S \rvert\)
    9 \(10^5\) \(10^5\) \(2 \times 10^5\) 对于所有询问有 \(l = 1, r = \lvert S \rvert\) 字符串随机
    10 \(2 \times 10^5\) \(10^5\) \(4 \times 10^5\) 对于所有询问有 \(l = 1, r = \lvert S \rvert\)
    11 \(2 \times 10^5\) \(10^5\) \(4 \times 10^5\) 对于所有询问有 \(l = 1, r = \lvert S \rvert\) 字符串随机
    12 \(3 \times 10^5\) \(10^5\) \(6 \times 10^5\) 对于所有询问有 \(l = 1, r = \lvert S \rvert\)
    13 \(3 \times 10^5\) \(10^5\) \(6 \times 10^5\) 对于所有询问有 \(l = 1, r = \lvert S \rvert\) 字符串随机
    14 \(4 \times 10^5\) \(10^5\) \(8 \times 10^5\) 对于所有询问有 \(l = 1, r = \lvert S \rvert\)
    15 \(4 \times 10^5\) \(10^5\) \(8 \times 10^5\) 对于所有询问有 \(l = 1, r = \lvert S \rvert\) 字符串随机
    16 \(5 \times 10^5\) \(10^5\) \(10^6\) 对于所有询问有 \(l = 1, r = \lvert S \rvert\)
    17 \(5 \times 10^5\) \(10^5\) \(10^6\) 对于所有询问有 \(l = 1, r = \lvert S \rvert\) 字符串随机
    18 \(2 \times 10^5\) \(10^5\) \(10^6\)
    19 \(3 \times 10^5\) \(10^5\) \(10^6\)
    20 \(4 \times 10^5\) \(10^5\) \(10^6\)
    21 \(5 \times 10^5\) \(10^5\) \(10^6\)
    22 \(5 \times 10^5\) \(10^5\) \(10^6\)
    23 \(5 \times 10^5\) \(10^5\) \(10^6\)
    24 \(5 \times 10^5\) \(10^5\) \(10^6\)
    25 \(5 \times 10^5\) \(10^5\) \(10^6\)

    对于所有数据,保证 \(1 \leq l \leq r \leq \lvert S \rvert\)\(1 \leq \lvert T \rvert \leq 5 \times 10^5\)

    Solution

    先简化题意:求 \(T\) 有多少个本质不同的子串,不是 \(S[l,r]\) 的子串。

    考虑对于每个 \(i\) 求出一个最小的 \(j\),满足 \(T[j,i]\)\(S[l,r]\) 的子串,那么对于任意 \(1\leq k<j\) 都满足 \(T[k,i]\) 不是 \(S[l,r]\) 的子串。显然随着 \(i\) 的增大,\(j\) 不会减小。那么考虑双指针做法:一开始令 \(j=1\)。对于每个 \(i\),让 \(j\) 的值不断增加,直到 \(T[j,i]\)\(S[l,r]\) 的子串。

    考虑如何快速模拟这个过程:先建出 \(S\) 串的后缀自动机。接着对于每次询问,维护一个指针 \(p\),一开始 \(p\) 指向后缀自动机的根节点。对于每个 \(i\),设 \(p\) 走字符 \(T[i]\) 的边到达的点为 \(u\)。接下来要判断节点 \(u\) 对应的长度为 \(i-j+1\) 字符串是否为 \(S[l,r]\) 的子串。

    考虑如何快速判断:一开始建后缀自动机的时候,对每个节点 \(x\) 维护两个标记 \(pos_x,yes_x\)。若 \(x\) 表示的其中一个字符串是 \(S\) 的前缀,前缀长度为 \(len\),则 \(pos_x=len,yes_x=1\),否则 \(pos_x\) 等于 \(parent\) 树上 \(x\) 子树中的任意一个 \(pos_x\)\(yes_x=0\)。显然 \(\lceil\) 节点 \(u\) 对应字符串的 \(right\) 集合\(\rfloor\) 就是 \(\lceil\) \(parent\) 树上 \(u\) 子树中 \(pos_x\) 的并集 \(\rfloor\)。而判断 \(u\) 对应的长度为 \(y\) 的字符串是否是 \(S[l,r]\) 的子串,也就是判断 \(u\) 子树中是否存在一个 \(pos_x∈[l+len-1,r]\)。这个可以用线段树合并或者主席树快速求出。

    回到原问题,若节点 \(u\) 对应的长度为 \(i-j+1\) 字符串是 \(S[l,r]\) 的子串,则令 \(p\) 指向节点 \(u\),令 \(lim_i=i-j+1\) 并结束循环。否则令 \(j\) 的值 \(+1\),为了保证 \(p\) 所在的节点能代表字符串 \(T[j,i-1]\),如果 \(i-j=maxl_{fa_p}\),令 \(p=fa_p\)

    因为求的是 \(T\) 中本质不同的合法子串有几个,所以把 \(T\) 串的后缀自动机也建出来。然后枚举这个后缀自动机上的每个节点 \(x\),令答案加上 \(max(0,maxl_x-max(lim_{pos_x},maxl_{fa_x}))\),含义是减去重复出现的子串以及非法的子串。

    时间复杂度 \(O(\sum|T|\log|S|)\)

    Code

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    
    using namespace std;
    
    const int e = 1e6 + 5;
    struct point
    {
    	int go[26];
    	inline void init()
    	{
    		memset(go, 0, sizeof(go));
    	}
    }t1[e], t2[e];
    struct node
    {
    	int l, r, cnt;
    }c[e * 20];
    char a[e];
    int nxt[e], go[e], adj[e], fa1[e], fa2[e], tot, n, m, maxl1[e], pos[e], id[e];
    int lim[e], lst, maxl2[e], num, rt[e], st[e], ed[e], pool, tim;
    bool yes[e];
    long long ans;
    
    inline void insert1(char ch)
    {
    	int c = ch - 'a', i = lst;
    	t1[lst = ++tot].init();
    	maxl1[lst] = maxl1[i] + 1;
    	yes[lst] = 1;
    	for (; i && !t1[i].go[c]; i = fa1[i]) t1[i].go[c] = lst;
    	if (!i) fa1[lst] = 1;
    	else
    	{
    		int j = t1[i].go[c];
    		if (maxl1[j] == maxl1[i] + 1) fa1[lst] = j;
    		else
    		{
    			int p;
    			t1[p = ++tot] = t1[j];
    			fa1[p] = fa1[j];
    			fa1[j] = fa1[lst] = p;
    			maxl1[p] = maxl1[i] + 1;
    			for (; i && t1[i].go[c] == j; i = fa1[i]) t1[i].go[c] = p; 
    		}
    	}
    }
    
    inline void insert2(char ch, int x)
    {
    	int c = ch - 'a', i = lst;
    	t2[lst = ++tot].init();
    	maxl2[lst] = maxl2[i] + 1;
    	pos[lst] = x;
    	for (; i && !t2[i].go[c]; i = fa2[i]) t2[i].go[c] = lst;
    	if (!i) fa2[lst] = 1;
    	else
    	{
    		int j = t2[i].go[c];
    		if (maxl2[j] == maxl2[i] + 1) fa2[lst] = j;
    		else
    		{
    			int p;
    			t2[p = ++tot] = t2[j];
    			fa2[p] = fa2[j];
    			pos[p] = x;
    			fa2[j] = fa2[lst] = p;
    			maxl2[p] = maxl2[i] + 1;
    			for (; i && t2[i].go[c] == j; i = fa2[i]) t2[i].go[c] = p; 
    		}
    	}
    }
    
    inline void add(int x, int y)
    {
    	nxt[++num] = adj[x];
    	adj[x] = num;
    	go[num] = y;
    }
    
    inline void insert(int y, int &x, int l, int r, int v)
    {
    	c[x = ++pool] = c[y];
    	c[x].cnt++;
    	if (l == r) return;
    	int mid = l + r >> 1;
    	if (v <= mid) insert(c[y].l, c[x].l, l, mid, v);
    	else insert(c[y].r, c[x].r, mid + 1, r, v);
    }
    
    inline int query(int x, int l, int r, int s, int t)
    {
    	if (!x) return 0;
    	if (l == s && r == t) return c[x].cnt;
    	int mid = l + r >> 1;
    	if (t <= mid) return query(c[x].l, l, mid, s, t);
    	else if (s > mid) return query(c[x].r, mid + 1, r, s, t);
    	else
    	return query(c[x].l, l, mid, s, mid) + query(c[x].r, mid + 1, r, mid + 1, t);
    }
    
    inline bool check(int x, int l, int r)
    {
    	return x && query(rt[ed[x]], 1, n, l, r) > query(rt[st[x] - 1], 1, n, l, r);
    }
    
    inline void dfs(int u)
    {
    	st[u] = ++tim;
    	id[tim] = u;
    	for (int i = adj[u]; i; i = nxt[i]) dfs(go[i]);
    	ed[u] = tim;
    }
    
    inline void build()
    {
    	int i;
    	for (i = 1; i <= n; i++) insert1(a[i]);
    	for (i = 2; i <= tot; i++) add(fa1[i], i);
    	dfs(1);
    	for (i = 1; i <= tot; i++)
    	if (yes[id[i]]) insert(rt[i - 1], rt[i], 1, n, maxl1[id[i]]);
    	else rt[i] = rt[i - 1];
    }
    
    int main()
    {
    	int i, l, r;
    	tot = lst = 1;
    	scanf("%s", a + 1);
    	n = strlen(a + 1);
    	build();
    	scanf("%d", &m);
    	while (m--)
    	{
    		tot = lst = 1;
    		t2[1].init();
    		int p = 1, nowl = 0;
    		scanf("%s%d%d", a + 1, &l, &r);
    		int len = strlen(a + 1);
    		for (i = 1; i <= len; i++)
    		{
    			int c = a[i] - 'a';
    			insert2(a[i], i);
    			for (;;)
    			{
    				if (check(t1[p].go[c], l + nowl, r))
    				{
    					nowl++;
    					p = t1[p].go[c];
    					break;
    				}
    				if (nowl == 0) break;
    				nowl--;
    				if (nowl == maxl1[fa1[p]]) p = fa1[p];
    			}
    			lim[i] = nowl;
    		}
    		ans = 0;
    		for (i = 2; i <= tot; i++)
    		ans += max(0, maxl2[i] - max(maxl2[fa2[i]], lim[pos[i]]));
    		printf("%lld\n", ans);
    	}
    	return 0;
    }
    
  • 相关阅读:
    HDU 2544 最短路
    HDU 3367 Pseudoforest
    USACO 2001 OPEN
    HDU 3371 Connect the Cities
    HDU 1301 Jungle Roads
    HDU 1879 继续畅通工程
    HDU 1233 还是畅通工程
    HDU 1162 Eddy's picture
    HDU 5745 La Vie en rose
    HDU 5744 Keep On Movin
  • 原文地址:https://www.cnblogs.com/cyf32768/p/12196028.html
Copyright © 2011-2022 走看看