CF504E Misha and LCP on Tree(樹鏈剖分+後綴樹組)
阿新 • • 發佈:2019-01-03
scan getchar() sca 我們 。。 i++ top sin num
1A真舒服。
喜聞樂見的樹鏈剖分+SA。
一個初步的想法就是用樹鏈剖分,把兩個字符串求出然後hash+二分lcp。。。不存在的。
我們用樹鏈剖分拼出兩個字符串(用樹剖拼出的這兩個字符串,一定是DFS序上一個一個區間拼在一起的,我們記錄這些區間的左右端點。)因為考慮到這個字符串是有序的,我們需要把每一條重鏈復制一遍並反著接在字符串後面構成一個新的DFS序。然後我們就是要求兩個區間的lcp,我們直接在整個DFS序上SA+ST表就行,求出lcp之後就在這些區間上分情況討論,然後就結束了。
具體看代碼,太醜了也看不出來什麽。
#include<iostream> #include<cstring> #include<cmath> #include<cstdio> #include<algorithm> using namespace std; const int N=601000; int cnt,head[N]; int size[N],fa[N],dep[N],dfn[N],top[N],tot,son[N]; int s[N],id[N][2],cn,Top[N]; int sa[N],x[N],y[N],len,rk[N],height[N],m,mn[N][20],c[N]; int L[N][2],R[N][2],NUM[N],Num[N],n; char S[N]; struct edge{ int to,nxt; }e[N]; void add(int u,int v){ cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; head[u]=cnt; } void dfs1(int u,int f){ size[u]=1; fa[u]=f; dep[u]=dep[f]+1; int maxson=-1; for(int i=head[u];i;i=e[i].nxt){ int v=e[i].to; if(v==f)continue; dfs1(v,u); if(size[v]>maxson){ maxson=size[v]; son[u]=v; } size[u]+=size[v]; } } void dfs2(int u,int tp){ dfn[u]=++tot; top[u]=tp; if(son[u])dfs2(son[u],tp); for(int i=head[u];i;i=e[i].nxt){ int v=e[i].to; if(v==fa[u]||v==son[u])continue; Top[++cn]=v; dfs2(v,v); } } void build(int u){ s[++len]=S[u]; id[dfn[u]][0]=len; if(son[u]==0){ id[dfn[u]][0]=len; s[++len]=S[u]; id[dfn[u]][1]=len; return; } build(son[u]); s[++len]=S[u]; id[dfn[u]][1]=len; } void get_sa(){ for(int i=1;i<=len;i++)c[x[i]=s[i]]++; for(int i=1;i<=m;i++)c[i]+=c[i-1]; for(int i=len;i>=1;i--)sa[c[x[i]]--]=i; for(int k=1;k<=len;k<<=1){ int num=0; for(int i=len-k+1;i<=len;i++)y[++num]=i; for(int i=1;i<=len;i++)if(sa[i]>k)y[++num]=sa[i]-k; for(int i=1;i<=m;i++)c[i]=0; for(int i=1;i<=len;i++)c[x[i]]++; for(int i=1;i<=m;i++)c[i]+=c[i-1]; for(int i=len;i>=1;i--)sa[c[x[y[i]]]--]=y[i],y[i]=0; for(int i=1;i<=len;i++)swap(x[i],y[i]); x[sa[1]]=1;num=1; for(int i=2;i<=len;i++) x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])?num:++num; if(num==len)break; m=num; } } void get_height(){ int k=0; for(int i=1;i<=len;i++)rk[sa[i]]=i; for(int i=1;i<=len;i++){ if(rk[i]==1)continue; if(k)k--; int j=sa[rk[i]-1]; while(i+k<=len&&j+k<=len&&s[i+k]==s[j+k])k++; height[rk[i]]=k; } } void pre_work(){ for(int i=1;i<=len;i++)mn[i][0]=height[i]; int Len=log2(len); for(int j=1;j<=Len;j++) for(int i=1;i+(1<<j)-1<=len;i++) mn[i][j]=min(mn[i][j-1],mn[i+(1<<j-1)][j-1]); } int get_lcp(int l,int r){ if(l>r)swap(l,r); l++; if(l>r)return 1e9; int Len=log2(r-l+1); return min(mn[l][Len],mn[r-(1<<Len)+1][Len]); } void work(int x,int y,int k){ while(top[x]!=top[y]){ if(dep[top[x]]>dep[top[y]]){ Num[k]++; L[Num[k]][k]=id[dfn[x]][1]; R[Num[k]][k]=id[dfn[top[x]]][1]; x=fa[top[x]]; } else{ NUM[k]--; L[NUM[k]][k]=id[dfn[top[y]]][0]; R[NUM[k]][k]=id[dfn[y]][0]; y=fa[top[y]]; } } if(dep[x]>dep[y]){ Num[k]++; L[Num[k]][k]=id[dfn[x]][1]; R[Num[k]][k]=id[dfn[y]][1]; } else{ NUM[k]--; L[NUM[k]][k]=id[dfn[x]][0]; R[NUM[k]][k]=id[dfn[y]][0]; } } void calc(){ int now0=1,now1=1; int tmp=0; while(now0<=n&&now1<=n){ if(now0>Num[0]&&now0<NUM[0])now0=NUM[0]; if(now1>Num[1]&&now1<NUM[1])now1=NUM[1]; if(now0>n||now1>n)break; int LL=min(min(R[now0][0]-L[now0][0]+1,R[now1][1]-L[now1][1]+1),get_lcp(rk[L[now0][0]],rk[L[now1][1]])); L[now0][0]=L[now0][0]+LL; L[now1][1]=L[now1][1]+LL; tmp+=LL; if(L[now0][0]<=R[now0][0]&&L[now1][1]<=R[now1][1])break; if(L[now0][0]>R[now0][0])now0++; if(L[now1][1]>R[now1][1])now1++; } printf("%d\n",tmp); } int read(){ int sum=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();} return sum*f; } int main(){ n=read(); scanf("%s",S+1); for(int i=1;i<n;i++){ int u=read(),v=read(); add(u,v);add(v,u); } dfs1(1,0);cn=1;Top[1]=1;dfs2(1,1); for(int i=1;i<=cn;i++)build(Top[i]); m=122; get_sa();get_height();pre_work(); m=read(); int a,b,c,d; while(m--){ a=read();b=read(); c=read();d=read(); Num[0]=Num[1]=0; NUM[0]=NUM[1]=n+1; work(a,b,0);work(c,d,1); calc(); } return 0; }
CF504E Misha and LCP on Tree(樹鏈剖分+後綴樹組)