P3435 [POI2006]OKR-Periods of Words
Pro
定义一个串A是Q的周期,仅当A是Q的proper前缀(即A!=Q的意思),而且Q是AA的前缀(没有proper的限制)。比如说abab和ababab是abababa的周期。
给你一个字符串,求出它所有前缀的最大周期长度之和。
Sol
首先先把A,Q画出来。
发现绿色部分是相等的,而且绿色部分是A的真border(既是A的前缀又是A的后缀而且不等于A)。
发现周期长度为len(Q)−len(border),于是想让周期尽量长,就要border尽量短。
问题转化为求A的每个前缀的最短非空border。
这就提示我们建立fail树,何谓fail树,就是i向next[i]连边形成的树形结构。
发现i到根节点的路径上的节点代表A[1→i]的所有border。
具体证明参见这篇博客。
建出fail树之后,发现对于和0相邻的所有节点,它的子树里面的所有节点的最短非空border的长度都为这个节点的标号,很好理解。
于是可以通过一个类似于染色的方法,O(n)地计算出来对于每个节点的最短非空border的长度。
最后统计答案扫一遍即可,记得开long long。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| #include <bits/stdc++.h> #define MAXN 1000005 using namespace std; inline int read(){ int x=0,f=1; char ch=getchar(); while (ch<'0'||ch>'9'){ if (ch=='-') f=-1; ch=getchar(); } while (ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^'0'); ch=getchar(); } return x*f; } char a[MAXN]; int nex[MAXN]; vector<int>G[MAXN]; int num[MAXN]; inline void dfs(int u,int father,int color){ num[u]=color; for (register int i=0;i<G[u].size();++i){ if (color==0) dfs(G[u][i],u,G[u][i]); else dfs(G[u][i],u,color); } } int main(){ int n=read(); scanf("%s",a+1); int j=0; for (register int i=2;i<=n;++i){ while (j&&a[j+1]!=a[i]) j=nex[j]; if (a[j+1]==a[i]) ++j; nex[i]=j; } for (register int i=1;i<=n;++i){ G[nex[i]].push_back(i); } dfs(0,0,0); long long ans=0; for (register int i=1;i<=n;++i){ ans+=i-num[i]; } printf("%lld\n",ans); }
|
P4035 [JSOI2008]球形空间产生器
假设我们设答案坐标(x1,x2,⋯ ,xn)(x_1,x_2, \cdots ,x_n)(x1,x2,⋯,xn),对于两个点(a1,a2,⋯ ,an),(b1,b2,⋯ ,bn)(a_1,...
P4887 【模板】莫队二次离线(第十四分块(前体))
传送门
此题思路还是非常巧妙的。
考虑我们莫队的 Add 函数,本质上是往原本的区间里面加上一个数,然后算这个数的贡献。
比如说我们设 f(x,[l,r])f(x,[l,r])f(x,[l,r]...