Codeforces 1137F - Matches Are Not a Child's Play(LCT)
阿新 • • 發佈:2021-10-14
LCT 維護類 access 操作,《樹點染色模型》(
中的點肯定是最後一批被刪除的,並且刪除順序取決於它們與 \(r\) 的遠近。
維護(注意,這裡不需要寫 top tree,因為是維護鏈的資訊)。BIT 資訊的維護就按照套路在 LCT 時扣掉除了右子樹之外部分原來優先順序的貢獻,加上新貢獻即可。
考慮將一個點 \(x\) 的編號變為當前所有點編號最大值 \(+1\) 會對每個點的刪除時間產生怎麼樣的影響。由於編號最大的點肯定是最後一個被刪除的,因此我們不妨令編號最大的點為根,那麼可以發現,對於不在 \(x\) 到根這條路徑上的點,它們刪除的相對位置順序是不會發生變化的,因為刪除這樣的點時,肯定它們的兒子已經被刪除了,而它的父親肯定沒被刪除,因此 \(x\) 到根節點這條路徑上的點的刪除順序肯定不影響其它點的刪除順序,而由於此時 \(x\) 和原來的根節點 \(r\) 分別是所有點中,權值最大和第二大的點,因此 \(x\to r\)
思考如何維護這個東西,發現這東西有點類似於 P3703 樹點染色 的模型,修改一個點點權時,相當於將這個點先 access
一遍把 \(x\to r\) 這條鏈搞出來並賦成一個新的顏色,然後再 makeroot(x)
。因此我們嘗試用 LCT 維護這個東西,對於每個實鏈,我們給它一個優先值 \(v\),優先值越大表示這條鏈上的點越靠後被刪除,而同一條實鏈上的點的刪除順序則是按照深度的增加而遞減,因此考慮建一個 BIT,對於每種優先順序 \(v\) 維護當前有多少個點的優先順序為 \(v\),這樣查詢就做一遍字首和,減掉當前實鏈中深度比當前節點小的點數,這個可以通過 LCT 的 \(siz\)
時間複雜度 \(n\log^2n\),別看 LCT 常數大,加上個 BIT 的小常數後倒跑得飛快(
const int MAXN=2e5; int n,qu,hd[MAXN+5],to[MAXN*2+5],nxt[MAXN*2+5],ec=0,deg[MAXN+5]; void adde(int u,int v){to[++ec]=v;nxt[ec]=hd[u];hd[u]=ec;} int t[MAXN*2+5],col_cnt=0; void add(int x,int v){for(int i=x;i<=MAXN*2;i+=(i&(-i))) t[i]+=v;} int query(int x){int ret=0;for(int i=x;i;i&=(i-1)) ret+=t[i];return ret;} struct node{int ch[2],f,siz,rev_lz,col,col_lz;} s[MAXN+5]; void pushup(int k){s[k].siz=s[s[k].ch[0]].siz+s[s[k].ch[1]].siz+1;} int ident(int k){return ((s[s[k].f].ch[0]==k)?0:((s[s[k].f].ch[1]==k)?1:-1));} void connect(int k,int f,int op){s[k].f=f;if(~op) s[f].ch[op]=k;} void rotate(int x){ int y=s[x].f,z=s[y].f,dx=ident(x),dy=ident(y); connect(s[x].ch[dx^1],y,dx);connect(y,x,dx^1);connect(x,z,dy); pushup(y);pushup(x); } void tag_rev(int k){swap(s[k].ch[0],s[k].ch[1]);s[k].rev_lz^=1;} void tag_col(int k,int v){s[k].col_lz=v;s[k].col=v;} void pushdown(int k){ if(s[k].rev_lz){ if(s[k].ch[0]) tag_rev(s[k].ch[0]); if(s[k].ch[1]) tag_rev(s[k].ch[1]); s[k].rev_lz=0; } if(s[k].col_lz){ if(s[k].ch[0]) tag_col(s[k].ch[0],s[k].col_lz); if(s[k].ch[1]) tag_col(s[k].ch[1],s[k].col_lz); s[k].col_lz=0; } } void pushall(int k){if(~ident(k)) pushall(s[k].f);pushdown(k);} void splay(int k){ pushall(k); while(~ident(k)){ if(ident(s[k].f)==-1) rotate(k); else if(ident(s[k].f)==ident(k)) rotate(s[k].f),rotate(k); else rotate(k),rotate(k); } } void access(int k){ int pre=0; for(;k;pre=k,k=s[k].f){ splay(k); add(s[k].col,-s[k].siz+s[s[k].ch[1]].siz); add(col_cnt,s[k].siz-s[s[k].ch[1]].siz); s[k].ch[1]=pre;pushup(k); } } void makeroot(int k){ col_cnt++;access(k);splay(k); tag_rev(k);tag_col(k,col_cnt); } int ask(int k){splay(k);return query(s[k].col)-s[s[k].ch[0]].siz;} void dfs0(int x,int f){ s[x].f=f; for(int e=hd[x];e;e=nxt[e]){ int y=to[e];if(y==f) continue; dfs0(y,x); } } void calc(){ priority_queue<int,vector<int>,greater<int> > q; for(int i=1;i<=n;i++) if(deg[i]==1) q.push(i); while(!q.empty()){ int x=q.top();q.pop();col_cnt++; add(col_cnt,1);s[x].col=col_cnt; for(int e=hd[x];e;e=nxt[e]){ int y=to[e]; if((--deg[y])==1) q.push(y); } } } int main(){ scanf("%d%d",&n,&qu);for(int i=1;i<=n;i++) s[i].siz=1; for(int i=1,u,v;i<n;i++) scanf("%d%d",&u,&v),deg[u]++,deg[v]++,adde(u,v),adde(v,u); dfs0(n,0);calc(); while(qu--){ static char opt[13];scanf("%s",opt+1); if(opt[1]=='u'){int x;scanf("%d",&x);makeroot(x);} else if(opt[1]=='w'){int x;scanf("%d",&x);printf("%d\n",ask(x));} else{ int u,v;scanf("%d%d",&u,&v); printf("%d\n",(ask(u)<ask(v))?u:v); } // for(int i=1;i<=n;i++) printf("%d%c",ask(i)," \n"[i==n]); } return 0; }