BZOJ3238: [Ahoi2013]差异

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$

(坑,未填。。。)

posted @ 2018-08-27 17:35  符拉迪沃斯托克  阅读(253)  评论(0编辑  收藏  举报
Live2D