BZOJ3238: [Ahoi2013]差异
Description
Input
一行,一个字符串S
Output
一行,一个整数,表示所求值
Sample Input
cacao
Sample Output
54
HINT
2<=N<=500000,S由小写英文字母组成
题解Here!
看到$LCP(T_i,T_j)$就想起了后缀数组。
先把式子变个形:
题目要求:$$Ans=\sum_{i=1}^{n-1}\sum_{j=i+1}^n(len(T_i)+len(T_j)-2\times LCP(T_i,T_j))$$
即:$$Ans=\sum_{i=1}^{n-1}\sum_{j=i+1}^n(len(T_i)+len(T_j))-2\times \sum_{i=1}^{n-1}\sum_{j=i+1}^nLCP(T_i,T_j)$$
设$Ans=S-2T$
我们知道:$T_i=suffix(i)$。
那么:$len(T_i)=n-i+1$
所以:$$S=\sum_{i=1}^{n-1}\sum_{j=i+1}^n(n-i+1+n-j+1)$$
即:$$S=\sum_{i=1}^{n-1}\sum_{j=i+1}^n(i+j)=\frac{n(n-1)(n+1)}{2}$$
所以问题就变成了怎么求$T$。
对于每一个$height[i]$,若$height[i-1]<=height[i]$,那么$height[i-1]$能取到的值$height[i]$都能取到。
若$height[i-1]>height[i]$,这部分的$LCP$长度就是$height[i]$。
所以我们用类似$DP$的思想,用一个单调栈维护前面距$i$最近且小于等于$height[i]$的元素,记为$p$。
则转移方程为:$$f[i]=f[p]+(i-p)\times height[i]$$
于是我们就有:$$T=\sum_{i=1}^nf[i]$$
然后就可以随便跑了。
附代码:
#include<iostream> #include<algorithm> #include<cstdio> #include<cmath> #include<stack> #include<cstring> #define MAXN 500010 using namespace std; int n; long long sum,f[MAXN]; char str[MAXN]; int top=0,sa[MAXN],rk[MAXN],tax[MAXN],tp[MAXN],height[MAXN]; struct node{ int val,id; }; stack<node> s; inline int read(){ int date=0,w=1;char c=0; while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();} while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();} return date*w; } void radixsort(){ for(int i=0;i<=top;i++)tax[i]=0; for(int i=1;i<=n;i++)tax[rk[i]]++; for(int i=1;i<=top;i++)tax[i]+=tax[i-1]; for(int i=n;i>=1;i--)sa[tax[rk[tp[i]]]--]=tp[i]; } void suffixsort(){ top=30; for(int i=1;i<=n;i++){ rk[i]=str[i]-'a'+1; tp[i]=i; } radixsort(); for(int w=1,p=0;p<n;top=p,w<<=1){ p=0; for(int i=1;i<=w;i++)tp[++p]=n-w+i; for(int i=1;i<=n;i++)if(sa[i]>w)tp[++p]=sa[i]-w; radixsort(); swap(rk,tp); rk[sa[1]]=p=1; for(int i=2;i<=n;i++) rk[sa[i]]=(tp[sa[i-1]]==tp[sa[i]]&&tp[sa[i-1]+w]==tp[sa[i]+w])?p:++p; } } void getheight(){ for(int i=1,j,k=0;i<=n;i++){ if(k)k--; j=sa[rk[i]-1]; while(str[i+k]==str[j+k])k++; height[rk[i]]=k; } } void work(){ int pos=0; long long ans=0; for(int i=1;i<=n;i++){ int p=pos; while(!s.empty()&&s.top().val>height[i])s.pop(); if(!s.empty())p=s.top().id; f[i]=f[p]+(i-p)*height[i]; ans+=f[i]; if(!height[i])pos=i; s.push((node){height[i],i}); } printf("%lld\n",sum-ans*2LL); } void init(){ scanf("%s",str+1); n=strlen(str+1); suffixsort(); getheight(); sum=1LL*n*(n-1)*(n+1)/2; } int main(){ init(); work(); return 0; }
强力解法:$SAM$
(坑,未填。。。)