日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

YbtOJ#532-往事之树【广义SAM,线段树合并】

發布時間:2023/12/3 编程问答 44 豆豆
生活随笔 收集整理的這篇文章主要介紹了 YbtOJ#532-往事之树【广义SAM,线段树合并】 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

正題

題目鏈接:https://www.ybtoj.com.cn/problem/532


題目大意

給出nnn個點的一個TrieTrieTrie樹,定義SxS_xSx?表示節點xxx代表的字符串
max{∣LCP(Sx,Sy)∣+∣LCS(Sx,Sy)∣}(x≠y)max\{|LCP(S_x,S_y)|+|LCS(S_x,S_y)|\}(x\neq y)max{LCP(Sx?,Sy?)+LCS(Sx?,Sy?)}(x?=y)
LCP/LCSLCP/LCSLCP/LCS分別表示最長公共前/后綴)

1≤n≤2×1051\leq n\leq 2\times 10^51n2×105


解題思路

正解好像是樹上SASASA+線段樹合并的做法可是我不會,就寫了廣義SAMSAMSAM

SAMSAMSAMparentsparentsparents樹就是后綴樹,這里給出了TrieTrieTrie樹就是一個構造廣義SAMSAMSAM的好條件。
構造出來的廣義SAMSAMSAM上的parentsparentsparents樹上的LCALCALCAlenlenlen就是兩個串的LCSLCSLCS
TrieTrieTrie樹上的LCALCALCA深度就是兩個串的LCPLCPLCP

考慮枚舉TrieTrieTrie樹上的一個點xxx,求它的子樹中LCSLCSLCS最大的一個點對,也就是后綴樹上LCALCALCA最深。

對后綴樹求一個dfsdfsdfs序,那么最優的點對都只會出現在相鄰的點對中,這樣的點對數量不會很多,可以考慮一種枚舉的方法。

可以使用線段樹合并,然后合并的時候每個節點維護一個改區間內最左/右的值。然后拿左區間的最右值和右區間的最左值計算答案就好了。

時間復雜度O(nlog?2n)O(n\log^2n)O(nlog2n),如果肯寫STSTST表求LCALCALCA可以做到O(nlog?n)O(n\log n)O(nlogn)


code

#include<cstdio> #include<cstring> #include<algorithm> #include<vector> #include<map> using namespace std; const int N=4e5+10,T=20,M=N<<5; struct node{int to,next,w; }a[N]; int n,tot,cnt,ans,ls[N],len[N],fa[N],f[N][T+1]; int rfn[N],dfn[N],g[N],rt[N],dep[N],p[N]; vector<int>G[N];map<int,int>ch[N]; void addl(int x,int y,int w){a[++tot].to=y;a[tot].next=ls[x];ls[x]=tot;a[tot].w=w;return; } int Insert(int p,int c){int np=++cnt;len[np]=len[p]+1;for(;p&&!ch[p][c];p=fa[p])ch[p][c]=np;if(!p)fa[np]=1;else{int q=ch[p][c];if(len[p]+1==len[q])fa[np]=q;else{int nq=++cnt;ch[nq]=ch[q];len[nq]=len[p]+1;fa[nq]=fa[q];fa[np]=fa[q]=nq;for(;p&&ch[p][c]==q;p=fa[p])ch[p][c]=nq;}}return np; } void dfs(int x){for(int i=ls[x];i;i=a[i].next){int y=a[i].to;p[y]=Insert(p[x],a[i].w);dfs(y);}return; } void dfs2(int x){dfn[++cnt]=x;rfn[x]=cnt;for(int i=0;i<G[x].size();i++){dep[G[x][i]]=dep[x]+1;dfs2(G[x][i]);}return; } int LCA(int x,int y){x=dfn[x];y=dfn[y];if(dep[x]>dep[y])swap(x,y);for(int i=T;i>=0;i--)if(dep[f[y][i]]>=dep[x])y=f[y][i];if(x==y)return x;for(int i=T;i>=0;i--)if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];return f[x][0]; } struct SegTree{int cnt,ls[M],rs[M],l[M],r[M];void Change(int &x,int L,int R,int pos){if(!x)x=++cnt;l[x]=r[x]=pos;if(L==R)return;int mid=(L+R)>>1;if(pos<=mid)Change(ls[x],L,mid,pos);else Change(rs[x],mid+1,R,pos);return;}int Merge(int &x,int y,int L,int R){if(!x||!y){x=x|y;return 0;}int mid=(L+R)>>1,ans=0;ans=Merge(ls[x],ls[y],L,mid);ans=max(ans,Merge(rs[x],rs[y],mid+1,R));l[x]=ls[x]?l[ls[x]]:l[rs[x]];r[x]=rs[x]?r[rs[x]]:r[ls[x]];if(ls[x]&&rs[x])ans=max(ans,len[LCA(r[ls[x]],l[rs[x]])]);return ans;} }Tr; void dfs3(int x,int dep){Tr.Change(rt[x],1,cnt,rfn[p[x]]);for(int i=ls[x];i;i=a[i].next){int y=a[i].to;dfs3(y,dep+1);g[x]=max(g[x],g[y]);g[x]=max(g[x],Tr.Merge(rt[x],rt[y],1,cnt));}ans=max(ans,g[x]+dep);return; } int main() {freopen("recollection.in","r",stdin);freopen("recollection.out","w",stdout);scanf("%d",&n);for(int i=2;i<=n;i++){int x,w;scanf("%d%d",&x,&w);addl(x,i,w);} cnt=p[1]=1;dfs(1);for(int i=2;i<=cnt;i++)G[fa[i]].push_back(i),f[i][0]=fa[i];cnt=0;dfs2(1);for(int j=1;j<=T;j++)for(int i=1;i<=cnt;i++)f[i][j]=f[f[i][j-1]][j-1];dfs3(1,0);printf("%d\n",ans); }

總結

以上是生活随笔為你收集整理的YbtOJ#532-往事之树【广义SAM,线段树合并】的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。