bzoj4598 [Sdoi2016]模式字串(hash+點分治)
阿新 • • 發佈:2019-01-29
分析:
統計樹上所有路徑,自然想到點分治,而字串比較,可以用hash的辦法
想到之前hu測的時候出現的奇技淫巧,但是並不會用(hash值之和為0)
在統計答案時候,方向不同答案不同:
所以我們在點分治的時候需要維護兩個方向的路徑
而且我們在統計路徑的時候,只關心是若干個模式串的字首或字尾的路徑
現在我們的問題就是:如何判斷一個字串是不是模式串的字首或字尾
顯然我們需要hash啦
tip
這道題真的有許多稀奇古怪的優化方法:
為保證複雜度,遞迴至子樹大小不足m時結束,時間複雜度
隨意%一發:doc給的標解是的,CA爺在一句話題解中給了的做法
#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
using namespace std;
const ull p=2000001001;
const int N=1000005;
int n,m,st[N],tot=0,F[N],size[N],root,sz,tt,tt1;
int S[N],S1[N],len[N],cnt[N],cnt1[N];
ull mi[N],a[N],a1[N],b[N],c[N],val[N],sum[N],sum1[N];
ll ans;
char s [N];
bool vis[N];
struct node{
int y,nxt;
};
node way[N<<1];
void add(int u,int w) {
tot++;way[tot].y=w;way[tot].nxt=st[u];st[u]=tot;
tot++;way[tot].y=u;way[tot].nxt=st[w];st[w]=tot;
}
void findroot(int now,int fa) {
size[now]=1;
F[now]=0;
for (int i=st[now];i;i=way[i].nxt)
if (way[i].y!=fa&&!vis[way[i].y]) {
findroot(way[i].y,now);
size[now]+=size[way[i].y];
F[now]=max(F[now],size[way[i].y]);
}
F[now]=max(F[now],sz-size[now]);
if (F[now]<F[root]) root=now;
}
void dfs(int now,int fa) { //正向
if (b[len[now]]==sum[now]&&val[now]==a[1]) S[++tt]=now;
for (int i=st[now];i;i=way[i].nxt)
if (way[i].y!=fa&&!vis[way[i].y]) {
sum[way[i].y]=sum[now]*p+val[way[i].y];
len[way[i].y]=len[now]+1;
dfs(way[i].y,now);
}
}
void dfs_1(int now,int fa) { //反向
if (c[len[now]]==sum1[now]&&val[now]==a1[1]) S1[++tt1]=now;
for (int i=st[now];i;i=way[i].nxt)
if (way[i].y!=fa&&!vis[way[i].y]) {
sum1[way[i].y]=sum1[now]*p+val[way[i].y];
dfs_1(way[i].y,now);
}
}
void calc(int now) {
for (int i=0;i<=m;i++) cnt[i]=0,cnt1[i]=0;
if (a[1]==val[now]) cnt[1]=1; //長度為i的正向鏈
if (a[m]==val[now]) cnt1[1]=1; //長度為i的反向鏈
if (m==1) ans+=cnt[1];
for (int i=st[now];i;i=way[i].nxt)
if (!vis[way[i].y]) {
tt=0; len[way[i].y]=1;
sum[way[i].y]=val[way[i].y];
dfs(way[i].y,now);
for (int j=1;j<=tt;j++) {
int t=S[j];
int pos=m-(len[t]-1)%m-1;
if (pos==0) pos+=m;
ans+=(ll)cnt1[pos];
}
tt1=0;
sum1[way[i].y]=val[way[i].y];
dfs_1(way[i].y,now);
for (int j=1;j<=tt1;j++) {
int t=S1[j];
int pos=m-(len[t]-1)%m-1;
if (pos==0) pos+=m;
ans+=(ll)cnt[pos];
}
for (int j=1;j<=tt;j++) {
int t=S[j];
int pos=len[t]%m+1;
if (val[now]==a[pos]) cnt[pos]++;
}
for (int j=1;j<=tt1;j++) {
int t=S1[j];
int pos=len[t]%m+1;
if (val[now]==a1[pos]) cnt1[pos]++;
}
}
}
void solve(int now) {
calc(now);
vis[now]=1;
for (int i=st[now];i;i=way[i].nxt)
if (!vis[way[i].y]) {
sz=size[way[i].y];
root=0;
if (sz<m) continue;
findroot(way[i].y,now);
solve(root);
}
}
int main()
{
int T;
scanf("%d",&T);
mi[0]=1;
for (int i=1;i<=1000000;i++) mi[i]=mi[i-1]*p;
while (T--) {
memset(st,0,sizeof(st)); tot=0;
ans=0;
scanf("%d%d",&n,&m);
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 u,w;
scanf("%d%d",&u,&w);
add(u,w);
}
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));
sz=n; root=0; F[0]=N;
findroot(1,0);
solve(root);
printf("%lld\n",ans);
}
return 0;
}