1. 程式人生 > >bzoj4598 [Sdoi2016]模式字串(hash+點分治)

bzoj4598 [Sdoi2016]模式字串(hash+點分治)

題目連結

分析:
統計樹上所有路徑,自然想到點分治,而字串比較,可以用hash的辦法

想到之前hu測的時候出現的奇技淫巧,但是並不會用(hash值之和為0)

在統計答案時候,方向不同答案不同:
這裡寫圖片描述

所以我們在點分治的時候需要維護兩個方向的路徑
而且我們在統計路徑的時候,只關心是若干個模式串的字首或字尾的路徑

現在我們的問題就是:如何判斷一個字串是不是模式串的字首或字尾
顯然我們需要hash啦

tip

這道題真的有許多稀奇古怪的優化方法:
為保證複雜度,遞迴至子樹大小不足m時結束,時間複雜度O(Tnlog(n/m))

隨意%一發:doc給的標解是

O(nlogn)的,CA爺在一句話題解中給了O(n)的做法

#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; }