LOJ#6463 AK YOI 樹分治+線段樹合並
阿新 • • 發佈:2018-07-28
int bits spa while std 後綴 距離 常數 back
傳送門
既然是樹上路徑統計問題,不難想到要使用樹分治,這裏以點分治為例
由點分治的性質,每層只需要考慮經過重心的路徑
因為需要維護路徑長度在一定範圍內的最大權值和,所以要用一個數據結構維護一下到根節點距離在一定範圍內的最大權值和
顯然線段樹是一個不錯的選擇,對每個子樹建立一個線段樹,根節點的答案用每個子樹的線段樹都更新一遍即可
考慮更新子樹中的點的答案,這時需要使用除這棵子樹外的所有子樹的線段樹一起更新
我們可以使用線段樹合並來維護,給子樹任意確定一個順序,然後通過維護每個子樹的前綴和後綴線段樹的並即可快速得到除去某棵子樹後的線段樹
顯然復雜度是有保證的,因為線段樹合並的復雜度無論如何都不會高於暴力插入三遍
總復雜度\(O(n\log^2 n)\),常數應該不小,不過跑的挺快233333
#include<bits/stdc++.h> using namespace std; const int maxn=100005,maxm=maxn*100; const long long INF=0x5f5f5f5f5f5f5f5fll; void solve(int,int); int getcenter(int,int); int getdis(int); void getans(int,int); void modify(int,int,int&); int merge(int,int); long long query(int,int,int); long long mx[maxm]; int lc[maxm],rc[maxm],cnt=0,root[maxn],prefix[maxn],suffix[maxn]; vector<int>G[maxn]; bool vis[maxn]; long long w[maxn],ans[maxn],ant[maxn],tmp; int p[maxn],size[maxn],son[maxn],q[maxn],d[maxn],pr[maxn],nx[maxn]; int n,m,L,R,val[maxn],s,t; int main(){ mx[0]=-INF; scanf("%d%d%d",&n,&L,&R); for(int i=1;i<=n;i++){ scanf("%d",&val[i]); ans[i]=-3472328296227680305ll; } for(int i=1,x,y;i<n;i++){ scanf("%d%d",&x,&y); G[x].push_back(y); G[y].push_back(x); } solve(1,n); for(int i=1;i<=n;i++){ if(i>1)printf(" "); printf("%lld",ans[i]); } printf("\n"); return 0; } void solve(int x,int sz){ x=getcenter(x,sz); m=sz; vis[x]=true; w[x]=val[x]; d[x]=0; if(sz==1)return; s=0; tmp=w[x]; modify(0,m,root[x]); for(int i=0;i<(int)G[x].size();i++) if(!vis[G[x][i]]){ p[G[x][i]]=x; getdis(G[x][i]); } s=L;t=R; for(int i=0,last=0;i<(int)G[x].size();i++) if(!vis[G[x][i]]){ if(s<=m)ans[x]=max(ans[x],query(0,m,root[G[x][i]])); prefix[G[x][i]]=merge(prefix[last],root[G[x][i]]); pr[G[x][i]]=last; last=G[x][i]; } ant[x]=-INF; for(int i=(int)G[x].size()-1,last=0;~i;i--) if(!vis[G[x][i]]){ suffix[G[x][i]]=merge(suffix[last],root[G[x][i]]); nx[G[x][i]]=last; last=G[x][i]; } for(int i=0;i<(int)G[x].size();i++) if(!vis[G[x][i]])getans(G[x][i],val[x]); ans[x]=max(ans[x],ant[x]); root[x]=0; for(int i=0;i<(int)G[x].size();i++) if(!vis[G[x][i]]){ pr[G[x][i]]=nx[G[x][i]]=0; root[G[x][i]]=prefix[G[x][i]]=suffix[G[x][i]]=0; } cnt=0; for(int i=0;i<(int)G[x].size();i++) if(!vis[G[x][i]])solve(G[x][i],size[G[x][i]]); } int getcenter(int x,int s){ int head=0,tail=0; q[tail++]=x; while(head!=tail){ x=q[head++]; size[x]=1; son[x]=0; for(int i=0;i<(int)G[x].size();i++) if(!vis[G[x][i]]&&G[x][i]!=p[x]){ p[G[x][i]]=x; q[tail++]=G[x][i]; } } for(int i=tail-1;i;i--){ x=q[i]; size[p[x]]+=size[x]; if(size[x]>size[son[p[x]]])son[p[x]]=x; } x=q[0]; while(son[x]&&size[son[x]]>(s>>1))x=son[x]; return x; } int getdis(int x){ int head=0,tail=0,rt=x; q[tail++]=x; while(head!=tail){ x=q[head++]; s=d[x]=d[p[x]]+1; tmp=w[x]=w[p[x]]+val[x]; modify(0,m,root[rt]); size[x]=1; for(int i=0;i<(int)G[x].size();i++) if(!vis[G[x][i]]&&G[x][i]!=p[x]){ p[G[x][i]]=x; q[tail++]=G[x][i]; } } for(int i=tail-1;i;i--){ x=q[i]; size[p[x]]+=size[x]; } return d[q[tail-1]]; } void getans(int x,int v){ int head=0,tail=0,rt=merge(prefix[pr[x]],suffix[nx[x]]); q[tail++]=x; while(head!=tail){ x=q[head++]; s=L-d[x]; t=R-d[x]; if(t<0||s>m)ant[x]=-INF; else ant[x]=w[x]-v+query(0,m,rt); if(s<=0&&t>=0)ant[x]=max(ant[x],w[x]); for(int i=0;i<(int)G[x].size();i++) if(!vis[G[x][i]]&&G[x][i]!=p[x]){ p[G[x][i]]=x; q[tail++]=G[x][i]; } } for(int i=tail-1;~i;i--){ x=q[i]; ant[p[x]]=max(ant[p[x]],ant[x]); ans[x]=max(ans[x],ant[x]); } } void modify(int l,int r,int &rt){ if(!rt){ rt=++cnt; mx[rt]=-INF; lc[rt]=rc[rt]=0; } mx[rt]=max(mx[rt],tmp); if(l==r)return; int mid=(l+r)>>1; if(s<=mid)modify(l,mid,lc[rt]); else modify(mid+1,r,rc[rt]); } int merge(int x,int y){ if(!x||!y)return x|y; int z=++cnt; mx[z]=max(mx[x],mx[y]); lc[z]=merge(lc[x],lc[y]); rc[z]=merge(rc[x],rc[y]); return z; } long long query(int l,int r,int rt){ if(s<=l&&t>=r)return mx[rt]; int mid=(l+r)>>1; if(t<=mid)return query(l,mid,lc[rt]); if(s>mid)return query(mid+1,r,rc[rt]); return max(query(l,mid,lc[rt]),query(mid+1,r,rc[rt])); }
LOJ#6463 AK YOI 樹分治+線段樹合並