BZOJ3238: [Ahoi2013]差异
Description
.jpg)
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$
(坑,未填。。。)

浙公网安备 33010602011771号