[SDOI2016] 模式字符串 (BZOJ4598 & VIJOS1995)

首先直接点分+hash就可以做,每个点用hash判断是否为S重复若干次后的前缀或后缀,每个子树与之前的结果O(m)暴力合并。在子树大小<m时停止分治,则总复杂度为O(nlog(n/m))。

问题在于n<=1e6。据说有O(n)的DP做法?写点分的话需要一大波常数优化……据说SDOI现场写了这题的全卡常T了……注意BZOJ并没有大数据,如果常数够小的话可以去VIJOS提交。

#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long u64;
typedef u64 ll;
const int N=1e6+5;
struct edge{int v;edge*s;}e[N*2];
edge*l1=e,*h[N];
void ins(int u,int v){
	edge s={v,h[u]};
	*(h[u]=l1++)=s;
}
int q,n,m,s1,s2,s3,d=1,siz[N],f1[N],f2[N],f3[N],f4[N];
bool vis[N];
char t1[N],t2[N];
ll c1[N],c2[N];
u64 ans;
void dfs1(int u,int v){
	int s4=0;
	s3=max(s3,d++);
	siz[u]=1;
	for(edge*i=h[u];i;i=i->s)
		if(i->v!=v&&!vis[i->v]){
			dfs1(i->v,u);
			siz[u]+=siz[i->v];
			s4=max(s4,siz[i->v]);
		}
	if(max(s4,s1-siz[u])*2<=s1)
		s2=u;
	--d;
}
void dfs3(int u,int v,ll w){
	++d,w=w*223+t1[u];
	if(w==c1[d])++f1[d%m];
	if(w==c2[d])++f2[d%m];
	for(edge*i=h[u];i;i=i->s)
		if(i->v!=v&&!vis[i->v])
			dfs3(i->v,u,w);
	--d;
}
void dfs2(int u){
	s3=0,dfs1(u,0),u=s2;
	if(s3*2-1<m)return;
	for(int j=0;j<m;++j)
		f3[j]=f4[j]=0;
	f3[1]=f4[1]=1;
	for(edge*i=h[u];i;i=i->s)
		if(!vis[i->v]){
			for(int j=0;j<m;++j)
				f1[j]=f2[j]=0;
			dfs3(i->v,u,t1[u]);
			for(int j=0;j<m;++j)
				ans+=(u64)f1[j]*f4[(m-j+1)%m]+(u64)f2[j]*f3[(m-j+1)%m];
			for(int j=0;j<m;++j)
				f3[j]+=f1[j],f4[j]+=f2[j];
		}
	vis[u]=1;
	int s5=s1;
	for(edge*i=h[u];i;i=i->s)
		if(!vis[i->v]){
			s1=siz[i->v]<siz[u]?siz[i->v]:s5-siz[u];
			if(s1>=m)dfs2(i->v);
		}
}
struct buf{
	char z[1<<25],*s;
	buf():s(z){
		z[fread(z,1,sizeof z,stdin)]=0;
	}
	void pre(char*v){
		while(*s<48)++s;
		while(*s>32)*v++=*s++;
		*v=0;
	}
	operator int(){
		int x=0;
		while(*s<48)++s;
		while(*s>32)
			x=x*10+*s++-48;
		return x;
	}
}it;
int main(){
	q=it;
	while(q--){
		n=it,m=it,it.pre(t1+1);
		for(int i=1;i<=n;++i)
			h[i]=0,vis[i]=0;
		l1=e;
		for(int i=2;i<=n;++i){
			int u=it,v=it;
			ins(u,v),ins(v,u);
		}
		it.pre(t2+1);
		ll w=1;
		int i=1;
		int j=m;
		for(int k=1;k<=n;++k){
			c1[k]=c1[k-1]+w*t2[i];
			c2[k]=c2[k-1]+w*t2[j];
			w*=223;
			if(++i>m)i=1;
			if(--j<1)j=m;
		}
		ans=0,s1=n,dfs2(1);
		printf("%llu
",ans);
	}
}
原文地址:https://www.cnblogs.com/f321dd/p/6490066.html