zoukankan      html  css  js  c++  java
  • 「HNOI2019」JOJO(kmp+border理论)

    Address

    LOJ#3055

    Solution

    Part 1:不含 2 操作

    • 我们把相邻且相同的字母看作一段,令 (a[i]) 表示第 (i) 段的字母,(b[i]) 表示第 (i) 段的长度。
    • (nxt[i]) 表示 (S[1dots i]) 的最长 ( ext{border}) 长度。
    • 如果 (nxt[i]=j),那么有:(forall kin[1,j],a[k]=a[i-j+k],forall k∈[2,j],b[k]=b[i-j+k],b[1]le b[i-j+1])
    • (nxt) 数组的方法和 ( ext{kmp}) 类似。
    • 如果我们已经求出了 (nxt[1]sim nxt[i-1]),那么可以这样计算第 (i) 段字母增加的答案:
    • 设变量 (maxl),一开始令 (j=nxt[i-1],maxl=0),判断是否 (a[j+1]=a[i])
    • 如果是,那么第 (i) 段字母中,位置 (maxl+1sim b[j+1]) 的失配指针(不是 (nxt) 数组,是题目所求)都在第 (j+1) 段中。
    • 接着令 (maxl=max(maxl,b[j+1]),j=nxt[j]),直到 (j=0)(maxl=b[i])
    • 注意特判失配指针在第 (1) 段的情况。
    • 时间复杂度 (O(n))

    Part 2:正解

    • 考虑把所有操作建成一棵树。树上的每个节点都对应某个时刻的字符串。
    • 对于树上的每一条边 (x→y),都有边权 ((a,b)),表示 (y) 是由 (x) 在末尾加上 (b) 个字符 (a) 得到的。
    • 但是 ( ext{kmp}) 的时间复杂度是均摊的,也就是说,直接放在树上做,时间复杂度是错的。
    • 考虑优化 ( ext{kmp})(nxt) 的过程。
    • 一开始令 (ls=i-1)(j=nxt[i-1])
    • 如果 (j+1)(i) 失配了,分情况讨论:
    	if (ls - j == j - nxt[j])
    	{
    		int per = j - nxt[j];
    		ls = j % per + per;
    		j %= per;
    	}
    	else ls = j, j = nxt[j];
    
    • 给出一个结论:把字符串 (S) 的所有 ( ext{border}) 按长度排序后可分为 (O(log |S|)) 段,每段是一个等差数列。(后面会证明)
    • 考虑这样做的正确性:(j+1)(i) 失配了,由于 (ls-j=j-nxt[j]),所以 (S[1dots ls]) 存在长度为 (j-nxt[j]) 的周期。
    • 也就是说,在位置 (j+1-per,j+1-2per......) 还是失配的。所以直接让 (j\%=per) 即可。
    • (nxt[m-1],nxt[nxt[m-1]],......) 都是 (m-1)( ext{border})。所以跳 (nxt) 的过程中,每个时刻的 (j) 都是 (m-1)( ext{border})
    • 而上述情况中,(ls,j,nxt[j],nxt[nxt[j]],......) 形成了一个等差数列。
    • 所以令 (j\%=per) 就直接跳过这个等差数列了。
    • (ls-j e j-nxt[j]) 时,我们可以认为是跳过了 (ls) 所在的等差数列。
    • 因此每次都会跳过一个等差数列,跳的次数上限 (O(log i))
    • 也就是说,( ext{kmp}) 可以做到单次 (O(log n))
    • 总时间复杂度 (O(nlog n))

    Part 3:证明结论

    • 首先证明:字符串 (S[1dots n]) 的所有不小于 (frac{n}{2})( ext{border}) 长度组成一个等差数列。
    • (S) 的最小 ( ext{period}) 长度为 (p),若 (ple frac{n}{2}),则存在长度 (ge frac{n}{2})( ext{border})
    • 可得 (S) 有长度为 (kp(1le kle frac{n}{p})) 的周期,也就是说 (S) 有长度为 (n-kp)( ext{border})(k) 可以取 (1sim frac{n}{p}) 中的任意一个。
    • 而最小的 (n-kp) 肯定小于 (frac{n}{2})
    • 因为最小的 (n-kp) 就是 (n\% p),而 (ple frac{n}{2}),所以 (n\% p<frac{n}{2})
    • 因此所有 (ge frac{n}{2})( ext{border}) 都能表示成 (n-kp),证毕。
    • 接下来,将 (S[1dots n])( ext{border}) 按长度分类:([1,1],[2,3],[4,7],dots ,[2^{k-1},2^k-1],[2^k,n-1]),其中 (2^{k+1}>n)
    • 对于两个字符串 (u,v),若 (|u|=|v|),则记 (P(u,v)=left{k:k>frac{|u|}{2},u[1dots k]=v[|v|-k+1dots |v|] ight})
    • 长度在 ([2^{i-1},2^i]) 之间的 ( ext{border})(P(S[1dots 2^i],S[n-2^i+1dots n]))
    • 长度在 ([2^k,n-1]) 之间的 ( ext{border})(P(S,S))
    • 下面证明 (P(u,v)) 中的元素能组成一个等差数列:
    • 取出 (P(u,v)) 中最大的元素 (x),剩下的元素都是 (u[1dots x])( ext{border})
    • 根据:字符串 (S) 的所有不小于 (frac{|S|}{2})( ext{border}) 长度组成一个等差数列,可知 (P(u,v)) 中的元素能组成一个等差数列。
    • 证毕。

    Code

    #include <bits/stdc++.h>
    
    using namespace std;
    
    #define ll long long
    
    template <class t>
    inline void read(t & res)
    {
    	char ch;
    	while (ch = getchar(), !isdigit(ch));
    	res = ch ^ 48;
    	while (ch = getchar(), isdigit(ch))
    	res = res * 10 + (ch ^ 48);
    }
    
    template <class t>
    inline void print(t x)
    {
    	if (x > 9) print(x / 10);
    	putchar(x % 10 + 48);
    }
    
    inline char getch()
    {
    	char ch;
    	while (ch = getchar(), !isalpha(ch));
    	return ch;
    }
    
    const int e = 2e5 + 5, mod = 998244353;
    
    char c[e], d[e];
    int adj[e], lst[e], go[e], num, cnt[e], n, ans[e], pos[e], nxt[e], m, sum[e], len[e];
    int pre[e];
    
    inline void add(int &x, int y)
    {
    	(x += y) >= mod && (x -= mod);
    }
    
    inline void link(int x, int y, char z, int t)
    {
    	lst[++num] = adj[x]; adj[x] = num; go[num] = y;
    	c[num] = z; cnt[num] = t;
    }
    
    inline int ask(int l, int r)
    {
    	if (l > r) return 0;
    	return (ll)(r - l + 1) * (l + r) / 2 % mod;
    }
    
    inline void dfs(int u)
    {
    	for (int i = adj[u]; i; i = lst[i])
    	{
    		int v = go[i], x = cnt[i];
    		char y = c[i];
    		m++; sum[m] = 0; len[m] = x; d[m] = y;
    		pre[m] = pre[m - 1] + x;
    		if (m != 1)
    		{
    			int j = nxt[m - 1], ls = m - 1;
    			while (j > 0 && (d[j + 1] != d[m] || len[j + 1] != len[m]))
    			{
    				if (ls - j == j - nxt[j])
    				{
    					int per = j - nxt[j];
    					ls = j % per + per;
    					j %= per;
    				}
    				else ls = j, j = nxt[j];
    			}
    			if (j > 0 || (d[j + 1] == d[m] && len[j + 1] == len[m])) nxt[m] = j + 1;
    			else if (d[1] == d[m] && len[1] <= len[m]) nxt[m] = 1;
    			else nxt[m] = 0;
    			
    			j = nxt[m - 1]; ls = m - 1;
    			int mx = 0;
    			while (j > 0 && mx < len[m])
    			{
    				if (d[j + 1] == d[m] && len[j + 1] > mx)
    				{
    					add(sum[m], ask(pre[j] + mx + 1, pre[j] + min(len[j + 1], len[m])));
    					mx = len[j + 1];
    				}
    				if (ls - j == j - nxt[j])
    				{
    					int per = j - nxt[j];
    					ls = j % per + per;
    					j %= per;
    				}
    				else ls = j, j = nxt[j];
    			}
    			if (mx < len[m])
    			{
    				if (d[1] == d[m])
    				{
    					int mid = min(len[1], len[m]);
    					add(sum[m], ask(mx + 1, mid));
    					sum[m] = (sum[m] + (ll)(len[m] - max(mx, mid)) * len[1]) % mod;
    				}
    			}
    		}
    		else sum[1] = ask(0, len[1] - 1);
    		add(sum[m], sum[m - 1]);
    		ans[v] = sum[m];
    		dfs(v);
    		m--;
    	}
    }
    
    int main()
    {
    	read(n);
    	int i, op, x; char y;
    	for (i = 1; i <= n; i++)
    	{
    		read(op); read(x);
    		if (op == 1)
    		{
    			if (!x)
    			{
    				pos[i] = pos[i - 1];
    				continue;
    			}
    			y = getch();
    			pos[i] = i;
    			int z = pos[i - 1];
    			link(z, i, y, x);
    		}
    		else pos[i] = pos[x];
    	}
    	dfs(0);
    	for (i = 1; i <= n; i++) print(ans[pos[i]]), putchar('
    ');
    	return 0;
    }
    
  • 相关阅读:
    CUUG PostgreSQL中级认证PGCP首考圆满结束!
    OCP 063中文考试题库(cuug内部资料)第21题
    OCP 063中文考试题库(cuug内部资料)第20题
    OCP 063中文考试题库(cuug内部资料)第19题
    OCP 063中文考试题库(cuug内部资料)第17题
    cdq实现树状数组
    P3810 【模板】三维偏序(陌上花开) 题解(cdq分治模板)
    CSUST 递增数组2 题解(思维+分段考虑)
    E. Clear the Multiset 题解(分治+贪心)
    P5019 [NOIP2018 提高组] 铺设道路 题解(贪心+思维)
  • 原文地址:https://www.cnblogs.com/cyf32768/p/12305384.html
Copyright © 2011-2022 走看看