[HDU5659]CA Loves Substring/[JZOJ4705]knight
2016-08-16 21:16
176 查看
题目大意
给定一个长度为n的字符串(只包含数字),我们定义Fi表示在第i个字符后面断开后,两个字符串的本质不同的子串个数。请快速求出F。1≤n≤5×105
题目分析
这道题正着很难做(xdl好像在考场上做到了O(nlog2n))。我们考虑正难则反:本来我们是对每个i求有多少个本质不同的子串至少有一次出现不包括位置i,现在我们转化为求每次出现都包括i,然后使用总的本质不同子串个数减去它。
先考虑一个子串S,令它最小出现位置为minp,最大出现位置为maxp,长度为len,那么我们分情况讨论:
minp≤maxp−len,如果这样的话,这个串至少有两个出现位置是不重叠的,那么它对答案是没有任何贡献的。
minp>maxp−len,这时候这个子串就会区间[maxp−len,minp)的F做出1的贡献
但是子串的个数是O(n2)的,我们总不能依次枚举吧。像这种统计子串信息的题目,我们应该想到后缀自动机。但是后缀自动机里面,一个节点可能代表多个子串,那么情况就会变得复杂一点。
令节点x的最小出现位置为minp,最大出现位置为maxp,最小长度为minl,最大长度为maxl。前两个值可以使用类似统计size的方法按照拓扑序统计出来,第三个值就是parent(x)的len加1,第四个值就是len(x)。
首先如果minp≤maxp−maxl,那么这个节点对答案是没有贡献的。
否则这个节点对F的贡献相当于区间加上一个首项1共差1的等差数列,然后再区间加同一个值。对于区间(maxp−maxl,maxp−minl+1]的F,贡献就分别是1,2,3,4...(每次增加一个能够覆盖的子串)。对于区间(maxp−minl+1,minp)的F,贡献则是r−l+1(该节点所有子串都能覆盖这里)。
这里我们要支持加入一个等差数列,还有区间加一个值。这个不带log怎么做啊?
然而这里我们不一定要在线。考虑对F数组差分得到fi=Fi−Fi−1。那么区间加工差为1的等差数列,相当于给一个区间加上1,这个是可以使用前缀和统计的。后面那部分加同样的数的区间,我们之间单点修改它的结束位置与上一位的差(r−l+1)。
然后最后再次做一遍前缀和就是答案了。
时间复杂度O(n),如果还有疑惑可以参看一下代码实现。
好久没有打后缀自动机,都快要忘了,一个下午复习了一下,总算记起来了。
代码实现
#include <algorithm> #include <iostream> #include <cstdio> #include <cctype> #include <queue> using namespace std; const int MOD=1000000007; const int P=100013; const int N=500050; const int S=N<<1; const int C=10; int add ,f ,cnt ,a[S]; int n,suf,tot,sum,ans; char s ; struct node { int mxp,mip,mxl,mil,len,prt; int next[C]; }sam[S]; int insert(int last,int c,int pos) { int np=++tot,p=last,q; for (sam[np].mip=sam[np].mxp=pos,sam[np].len=sam[p].len+1;p&&!sam[p].next[c];p=sam[p].prt) sam[p].next[c]=np; if (!p) sam[np].prt=1; else if (sam[q=sam[p].next[c]].len==sam[p].len+1) sam[np].prt=q; else { int nq=++tot; sam[nq]=sam[q],sam[nq].len=sam[p].len+1; sam[nq].mip=n,sam[nq].mxp=-1; sam[q].prt=sam[np].prt=nq; for (;p&&sam[p].next[c]==q;p=sam[p].prt) sam[p].next[c]=nq; } return np; } void sorts() { for (int i=1;i<=tot;i++) cnt[sam[i].len]++; for (int i=1;i<=n;i++) cnt[i]+=cnt[i-1]; for (int i=1;i<=tot;i++) a[cnt[sam[i].len]--]=i; } int main() { freopen("knight.in","r",stdin),freopen("knight.out","w",stdout); scanf("%d",&n),scanf("%s",s),tot=suf=1; for (int i=0;i<n;i++) suf=insert(suf,s[i]-'0',i); for (int i=tot;i>1;i--) (sum+=(sam[i].len-sam[sam[i].prt].len))%=MOD; sorts(); for (int i=tot,x;i>1;i--) { sam[x=sam[a[i]].prt].mip=min(sam[x].mip,sam[a[i]].mip),sam[x].mxp=max(sam[x].mxp,sam[a[i]].mxp); sam[a[i]].mxl=sam[a[i]].len,sam[a[i]].mil=sam[x].len+1; } for (int i=2;i<=tot;i++) { int l=sam[i].mil,r=sam[i].mxl; l=max(l,sam[i].mxp-sam[i].mip+1); if (l>r) continue; add[sam[i].mxp-r+1]++; add[sam[i].mxp-l+2]--; (((f[sam[i].mip]-=(r-l+1))%=MOD)+=MOD)%=MOD; } for (int k=0,i=0;i<n;i++) (k+=add[i])%=MOD,(f[i]+=k)%=MOD; for (int i=1;i<n;i++) (f[i]+=f[i-1])%=MOD; for (int i=0;i<n;i++) f[i]=(sum-f[i]+MOD)%MOD; for (int i=n-2,pw=1;i>=0;i--,pw=1ll*pw*P%MOD) (ans+=1ll*f[i]*pw%MOD)%=MOD; printf("%d\n",ans); fclose(stdin),fclose(stdout); }
相关文章推荐
- hdu5659 CA Loves Substring
- HDU5659:CA Loves Substring(后缀自动机)
- CA Loves Substring(HDU 5659)
- HDU 5658 CA Loves Palindromic (回文树)
- HDU 5656 CA Loves GCD dp,常数优化
- BestCoder Round #78 CA Loves GCD
- hdu 5656 CA Loves GCD(dp+gcd)(Bestcoder #78 1002)
- HDU - CA Loves GCD
- hdu-5655 CA Loves Stick(水题)
- HDU - 5655 CA Loves Stick
- hdoj 5656 CA Loves GCD 【dp】
- HDU 5677 ztr loves substring(回文串加多重背包)
- HDU 5655 CA Loves Stick
- hdu5655 CA Loves Stick JAVA大数
- hdu 5655 CA Loves Stick(简单题)(Bestcoder #78 1001)
- HDU 5677 ztr loves substring(回文串加多重背包)
- hdu_5677_ztr loves substring(回文+二维多重背包)
- HDU 5656 CA Loves GCD
- HDU 5655 CA Loves Stick(思维题目)【bestcoder】
- JZOJ3332【NOI2013模拟】棋盘游戏 特判边界的计数问题(BZOJ 4705)