您的位置:首页 > 其它

bzoj 4598: [Sdoi2016]模式字符串 (hash+点分治)

2017-04-13 19:34 330 查看

题目描述

传送门

题目大意:给出一个n个节点的树,每个节点上有一个大写字母,给出一个模式串,求有多少路径是由模式串重复若干次得到的。

题解

hash+点分治。

对于每个点依次加入他的每个儿子的子树,然后计算当前儿子的子树与已经处理过的儿子的子树能形成多少合法串。

对于路径上行和下行都要考虑,用hash判断路径是否是模式串的前后缀。

注意细节。

时间复杂度应该是O(Tnlogn)

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 1000003
#define ull unsigned long long
#define p 2000001001
#define LL long long
#define inf 1000000000
using namespace std;
int n,m,T,n1,root,point
,nxt[N*2],v[N*2],tot,size
,f
,sz,sz1;
int st
,st1
,len
,cnt
,cnt1
;
ull mi
,a
,a1
,b
,c
,val
,sum
,sum1
;
LL ans;
bool vis
;
char s
;
int read()
{
char ch = getchar();
for ( ; ch > '9' || ch < '0'; ch = getchar());
int tmp = 0;
for ( ; '0' <= ch && ch <= '9'; ch = getchar())
tmp = tmp * 10 + int(ch) - 48;
return tmp;
}
void add(int x,int y)
{
tot++; nxt[tot]=point[x]; point[x]=tot; v[tot]=y;
tot++; nxt[tot]=point[y]; point[y]=tot; v[tot]=x;
}
void get_root(int x,int fa)
{
size[x]=1; f[x]=0;
for (int i=point[x];i;i=nxt[i]) {
if (v[i]==fa||vis[v[i]]) continue;
get_root(v[i],x);
f[x]=max(f[x],size[v[i]]);
size[x]+=size[v[i]];
}
f[x]=max(f[x],n1-size[x]);
if (f[x]<f[root]) root=x;
}
void get_deep(int x,int fa)
{
if (b[len[x]]==sum[x]&&val[x]==a[1]) st[++sz]=x;
for (int i=point[x];i;i=nxt[i]){
if (vis[v[i]]||v[i]==fa) continue;
sum[v[i]]=sum[x]*p+val[v[i]];
len[v[i]]=len[x]+1;
get_deep(v[i],x);
}
}
void get_deep1(int x,int fa)
{
if (c[len[x]]==sum1[x]&&val[x]==a1[1]) st1[++sz1]=x;
for (int i=point[x];i;i=nxt[i]) {
if(vis[v[i]]||v[i]==fa) continue;
sum1[v[i]]=sum1[x]*p+val[v[i]];
get_deep1(v[i],x);
}
}
void calc(int x)
{
for (int i=0;i<=m;i++) cnt[i]=0,cnt1[i]=0;
if (a[1]==val[x])cnt[1]=1;
if (a[m]==val[x])cnt1[1]=1;
if (m==1) ans+=cnt[1];
for (int i=point[x];i;i=nxt[i]) {
if (vis[v[i]]) continue;
sz=0; len[v[i]]=1; sum[v[i]]=val[v[i]];
get_deep(v[i],x);
for (int j=1;j<=sz;j++) {
int t=st[j]; int pos=m-(len[t]-1)%m-1;
if (pos==0) pos+=m;
ans+=(LL)cnt1[pos];
}
sz1=0; sum1[v[i]]=val[v[i]];
get_deep1(v[i],x);
for (int j=1;j<=sz1;j++) {
int t=st1[j]; int pos=m-(len[t]-1)%m-1;
if (pos==0) pos+=m;
ans+=(LL)cnt[pos];
}
for (int j=1;j<=sz;j++) {
int t=st[j]; int pos=(len[t])%m+1;
if (val[x]==a[pos]) cnt[pos]++;
}
for (int j=1;j<=sz1;j++) {
int t=st1[j]; int pos=(len[t])%m+1;
if (val[x]==a1[pos]) cnt1[pos]++;
}
}
}
void solve(int x)
{
calc(x); vis[x]=1;
for (int i=point[x];i;i=nxt[i]) {
if (vis[v[i]]) continue;
n1=size[v[i]]; root=0;
if (n1<m) continue;
get_root(v[i],x);
solve(root);
}
}
int main()
{
freopen("a.in","r",stdin);
//  freopen("my.out","w",stdout);
T=read(); mi[0]=1;
for (int i=1;i<=1000000;i++) mi[i]=mi[i-1]*p;
while (T--) {
n=read(); m=read(); tot=0; ans=0;
memset(point,0,sizeof(point));
scanf("%s",s+1);
for (int i=1;i<=n;i++) val[i]=s[i]-'A'+1;
for (int i=1;i<n;i++) {
int x,y; x=read(); y=read();
add(x,y);
}
scanf("%s",s+1);
for (int i=1;i<=max(n,m);i++) a[i]=s[(i-1)%m+1]-'A'+1;
for (int i=1;i<=max(n,m);i++) b[i]=b[i-1]+a[i]*mi[i-1];
for (int i=1;i<=m;i++) a1[m-i+1]=a[i];
for (int i=1;i<=max(n,m);i++) a1[i]=a1[(i-1)%m+1];
for (int i=1;i<=max(n,m);i++) c[i]=c[i-1]+a1[i]*mi[i-1];
memset(vis,0,sizeof(vis));
f[0]=inf; root=0; n1=n;
get_root(1,0);
solve(root);
printf("%lld\n",ans);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  hash 点分治