BZOJ 3238 AHOI2013 差异 后缀自动机

题目大意:给定一个字符串,求Σ[1<=i<j<=n]|Ti|+|Tj|-2|LCP(Ti,Tj)|

前两项是可以O(1)求的 我们要求的就是LCP之和

对反串建立后缀自动机 那么parent指针连成的树就是后缀树

直接在后缀树上DP就行- -

对于每个节点统计所有子树两两right集合大小乘积之和乘上这个节点的深度即可

QY神在学校讲了一天的SAM。。。 现在我觉得我还是回去学大型建筑机械吧233- -

#include <map>
#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 500500
using namespace std;
namespace Suffix_Automaton{
	struct SAM{
		map<int,SAM*> son;
		vector<SAM*> tree_son;
		SAM *parent;
		int max_dpt,right;
		bool mark;
		SAM(int _):parent(0x0),max_dpt(_),right(0),mark(false) {}
	}*root=new SAM(0),*last=root;
	void Extend(int x)
	{
		SAM *p=last;
		SAM *np=new SAM(p->max_dpt+1);
		for(;p&&!p->son[x];p=p->parent)
			p->son[x]=np;
		if(!p) np->parent=root;
		else
		{
			SAM *q=p->son[x];
			if(p->max_dpt+1==q->max_dpt)
				np->parent=q;
			else
			{
				SAM *nq=new SAM(p->max_dpt+1);
				nq->son=q->son;
				nq->parent=q->parent;
				q->parent=nq;np->parent=nq;
				for(;p&&p->son[x]==q;p=p->parent)
					p->son[x]=nq;
			}
		}
		last=np;
		np->right++;
	}
}
int n;
long long ans;
char s[M];
void DFS(Suffix_Automaton::SAM *p)
{
	using namespace Suffix_Automaton;
	map<int,SAM*>::iterator it;
	if(p->parent)
		p->parent->tree_son.push_back(p);
	p->mark=1;
	for(it=p->son.begin();it!=p->son.end();it++)
		if( !(it->second)->mark )
			DFS(it->second);
}
void Tree_DP(Suffix_Automaton::SAM *p)
{
	using namespace Suffix_Automaton;
	vector<SAM*>::iterator it;
	for(it=p->tree_son.begin();it!=p->tree_son.end();it++)
	{
		Tree_DP(*it);
		ans-=(long long)p->max_dpt*p->right*(*it)->right<<1;
		p->right+=(*it)->right;
	}
}
int main()
{
	using namespace Suffix_Automaton;
	int i;
	scanf("%s",s+1);n=strlen(s+1);
	for(i=n;i;i--)
		Extend(s[i]-'a');
	ans=(long long)(n-1)*n*(n+1)/2;
	DFS(root);Tree_DP(root);
	cout<<ans<<endl;
	return 0;
}


你可能感兴趣的:(后缀自动机,bzoj,BZOJ3238)