生活随笔
收集整理的這篇文章主要介紹了
[ZJOI2016]大森林
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
Description:
小Y家里有一個大森林,里面有n棵樹,編號從1到n
0 l r 表示將第 l 棵樹到第 r 棵樹的生長節點下面長出一個子節點,子節點的標號為上一個 0 號操作葉子標號加 1(例如,第一個 0 號操作產生的子節點標號為 2), l 到 r 之間的樹長出的節點標號都相同。保證 1<=l<=r<=n 。
1 l r x 表示將第 l 棵樹到第 r 棵樹的生長節點改到標號為 x 的節點。對于 i (l<=i<=r)這棵樹,如果標號 x的點不在其中,那么這個操作對該樹不產生影響。保證 1<=l<=r<=n , x 不超過當前所有樹中節點最大的標號。
2 x u v 詢問第 x 棵樹中節點 u 到節點 v 點的距離,也就是在第 x 棵樹中從節點 u 和節點 v 的最短路上邊的數量。保證1<=x<=n,這棵樹中節點 u 和節點 v 存在。
Hint:
$ N<=10^5,M<=2*10^5 $
Solution:
一開始想到線段樹套LCT,然而標記無法下傳
考慮樸素做法,每個節點用LCT維護一片森林,空間顯然無法承受
這時我們需要轉換思路
因為題目沒有強制在線,所以我們可以只維護一顆”樹“,然后按順序處理1、2操作
每次1操作直接把該生長節點的子樹”嫁接“到另一個生長節點
為了保證復雜度,我們需要在生長節點處開一個虛點,這樣就不需要多點換父親了
2操作直接用求個LCA就行了
#include<bits/stdc++.h>
using namespace std;
const int mxn=3e5+5;
struct Q {int pos,id,x,y,qr;
}q[mxn];
int n,m,p,s,tot,cnt,sum;
int t[mxn],lp[mxn],rp[mxn],bl[mxn],fa[mxn],ch[mxn][2],ans[mxn],val[mxn];int cmp(Q x,Q y) {return x.pos==y.pos?x.id<y.id:x.pos<y.pos;
}namespace lct {int isnotrt(int x) {return ch[fa[x]][0]==x||ch[fa[x]][1]==x;}void push_up(int x) {t[x]=t[ch[x][0]]+t[ch[x][1]]+val[x];}void rotate(int x) {int y=fa[x],z=fa[y],tp=ch[y][1]==x;if(isnotrt(y)) ch[z][ch[z][1]==y]=x; fa[x]=z;ch[y][tp]=ch[x][tp^1]; fa[ch[x][tp^1]]=y;ch[x][tp^1]=y; fa[y]=x;push_up(y),push_up(x);}void splay(int x) {while(isnotrt(x)) {int y=fa[x],z=fa[y];if(isnotrt(y)) (ch[y][1]==x)^(ch[z][1]==y)?rotate(x):rotate(y);rotate(x); }}int access(int x) {int y;for(y=0;x;x=fa[y=x]) splay(x),ch[x][1]=y,push_up(x);return y; }void link(int x,int y) {splay(x); fa[x]=y;}void cut(int x) {access(x); splay(x); ch[x][0]=fa[ch[x][0]]=0; push_up(x);}
}
using namespace lct;int main()
{scanf("%d%d",&n,&m); int res,lca,opt,l,r,x,y; p=tot=val[1]=t[1]=lp[1]=bl[1]=1; rp[1]=n; int now=2; link(++p,1);for(int i=1;i<=m;++i) {scanf("%d",&opt);if(opt==0) {scanf("%d%d",&l,&r);bl[++tot]=++p; link(p,now); //每次把新節點直接長在最近更新的生成節點,考慮這樣為什么不會錯,因為實點之間的虛點不會影響答案lp[tot]=l,rp[tot]=r; val[p]=t[p]=1;}else if(opt==1) {scanf("%d%d%d",&l,&r,&x); l=max(l,lp[x]); r=min(r,rp[x]); if(l>r) continue;//去掉無用的區間link(++p,now);q[++s]=(Q){l,i,p,bl[x],0};q[++s]=(Q){r+1,i,p,now,0};now=p;}else scanf("%d%d%d",&l,&x,&y),q[++s]=(Q){l,i,bl[x],bl[y],++sum};}sort(q+1,q+s+1,cmp); for(int i=1;i<=s;++i) {if(q[i].qr>0) {access(q[i].x); splay(q[i].x); res=t[q[i].x]; //因為這題規定根為1,所以我們不能makertlca=access(q[i].y); splay(q[i].y); res+=t[q[i].y];access(lca); ans[q[i].ss]=res-t[lca]*2; }else cut(q[i].x),link(q[i].x,q[i].y);}for(int i=1;i<=sum;++i) printf("%d\n",ans[i]);return 0;
}
轉載于:https://www.cnblogs.com/list1/p/10421740.html
總結
以上是生活随笔為你收集整理的[ZJOI2016]大森林的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。