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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

P4332 [SHOI2014]三叉神经树(LCT)

發(fā)布時間:2024/7/5 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 P4332 [SHOI2014]三叉神经树(LCT) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

Luogu4332

LOJ2187

題解

代碼-Tea

題意 : 每個點有三個兒子 , 給定葉節(jié)點的權(quán)值\(0\)\(1\)且支持修改 , 非葉子節(jié)點的權(quán)值為當有\(>=2\)個兒子的權(quán)值為\(1\)的時候取\(1\) , 每次修改后求根節(jié)點輸出
定義 : 權(quán)值可以取\(0,1,2,3\) ; 輸出\(0\)\(1\)權(quán)值\(>=2\)輸出\(1\) .
限制 : 修改的都是葉子節(jié)點

考慮如果把輸出\(0\)改成\(1\) , 則找到祖先中最深的權(quán)值不為\(1\)的點 , 這條鏈上的輸出會改變 , 這里用\(LCT\)維護
如果把輸出\(1\)改成\(0\) , 則找到祖先中最深的權(quán)值不為 \(2\) 的點 , 這條鏈上的輸出會改變?yōu)?span id="ozvdkddzhkzd" class="math inline">\(0\)

這樣有點麻煩 , 來一個轉(zhuǎn)化 , 葉節(jié)點權(quán)值取值范圍由\(0,1\)變?yōu)?span id="ozvdkddzhkzd" class="math inline">\(1,2\) ; 統(tǒng)一變成了權(quán)值\(2\)會對父親產(chǎn)生貢獻 , 權(quán)值\(1\)對父親沒有貢獻

定義\(not1[x]=true\)的意義是\(x\)的子樹中存在 權(quán)值不為\(1\)的點 , \(not2[]\)類似 .
只要寫了\(pushdown\) , \(LCT\)也能區(qū)間打標記 .

打標記時先潦草地搞一下有類似\(not2[x]=not1[x],not1[x]=0\)的操作 , 之后在\(pushup\)的時候會有簡單而正確的更新 : \(not1[x]=not1[ls]\ |\ not1[rs]\ |\ (val[x]!=1);\)
一定要想清楚所有狀況才能說明這是對的 : 如果把\(0\)改成\(1\) , 可能碰到一個權(quán)值\(0\)的點變成\(1\) , 或者\(2\)變成\(3\)而停止 ;
如果把\(1\)變成\(0\) , 可能碰到一個權(quán)值\(1\)的點變成\(0\) , 或者\(3\)變成\(2\)而停止 .

所以\(pushup\)\(LCT\)的關(guān)鍵 , 也是所有數(shù)據(jù)結(jié)構(gòu)的關(guān)鍵 , 盡管在一般線段樹題目中看不出來 ,
但在復雜一點的比如P4198樓房重建和 [HNOI/AHOI2018]轉(zhuǎn)盤便顯得非常重要了 .
類似地 , [NOIP2018]保衛(wèi)王國中從下往上轉(zhuǎn)移使得矩陣乘的更新順序改變 , 說明\(pushup\)操作真的非常重要.
最簡單的操作卻能更新很難更新的情況,從而維護正確性

這道題還用到了一個點 , \(access\)可以使無關(guān)的點斷開 , 而不直接\(fa[x]=0,ch[y][_]=0\)是因為\(access\)包括了\(pushup\)的更新
兩個端點找出來以后就可以用上面說的修改和打標記了

注釋更新于\(19.3.29\)

#include<bits/stdc++.h> #define debug(...) fprintf(stderr,__VA_ARGS__) #define Debug(x) cout<<#x<<"="<<x<<endl using namespace std; typedef long long LL; const int INF=1e9+7; inline LL read(){register LL x=0,f=1;register char c=getchar();while(c<48||c>57){if(c=='-')f=-1;c=getchar();}while(c>=48&&c<=57)x=(x<<3)+(x<<1)+(c&15),c=getchar();return f*x; }const int MAXN=1.5e6+5;int fa[MAXN],to[MAXN][3]; int n,m;namespace LCT{int par[MAXN],ch[MAXN][2],val[MAXN],tag[MAXN];bool not1[MAXN],not2[MAXN];//定義not1[x]=true的意義是x的子樹中存在權(quán)值不為1的點int st[MAXN],top; #define ls (ch[rt][0]) #define rs (ch[rt][1])inline bool chk(int x){return ch[par[x]][1]==x;}inline bool isnotroot(int x){return ch[par[x]][0]==x||ch[par[x]][1]==x;}inline void pushup(int rt){not1[rt]=not1[ls]|not1[rs]|(val[rt]!=1);//最簡單的維護卻可以更新很難更新的情況,從而維護正確性. pushup是LCT的關(guān)鍵not2[rt]=not2[ls]|not2[rs]|(val[rt]!=2);}inline void modify(int x,int y){val[x]+=y,tag[x]+=y;//區(qū)間樹上打標記if(y>0) not2[x]=not1[x],not1[x]=0;//對整段val[]=1的區(qū)間加1,val[i]=0變1的誤判會在pushup()里改正,然后就沒有別的情況了else not1[x]=not2[x],not2[x]=0;//這里的修改只是簡單的維護}inline void pushdown(int rt){if(!tag[rt]) return;if(ls) modify(ls,tag[rt]);if(rs) modify(rs,tag[rt]);tag[rt]=0;}inline void rotate(int x){int y=par[x],z=par[y],k=chk(x),w=ch[x][k^1];ch[y][k]=w,par[w]=y;if(isnotroot(y)) ch[z][chk(y)]=x; par[x]=z;ch[x][k^1]=y,par[y]=x;pushup(y);pushup(x);}inline void splay(int x){int y=x,top=0;while(1){st[++top]=y;if(isnotroot(y)) y=par[y];else break;}while(top) pushdown(st[top--]);while(isnotroot(x)){int y=par[x];if(isnotroot(y)){if(chk(x)==chk(y)) rotate(y);else rotate(x);}rotate(x);}}inline void access(int x){for(int y=0;x;x=par[y=x])splay(x),ch[x][1]=y,pushup(x);}inline int findnot1(int rt){pushdown(rt);if(!not1[rt]) return 0;if(not1[rs]) return findnot1(rs);//找到深度最大的不是1的點,即能夠影響到的最遠的點if(val[rt]!=1) return rt;return findnot1(ls);}inline int findnot2(int rt){pushdown(rt);if(!not2[rt]) return 0;if(not2[rs]) return findnot2(rs);if(val[rt]!=2) return rt;return findnot2(ls);} #undef ls #undef rs }using namespace LCT;inline void dfs(int u){if(u>n) return;//因為初始數(shù)據(jù)一定是合法的,所以沒必要往更深的地方走for(int i=0;i<=2;i++){int v=to[u][i];dfs(v);val[u]+=(val[v]>>1);//只有權(quán)值>=1的才有意義,先簡單搞一下貢獻} // val[u]>>=1; }int main(){ // freopen("asd.in","r",stdin);n=read();for(int i=1;i<=n;i++)for(int j=0;j<=2;j++){int x=read();to[i][j]=x;par[x]=i,fa[x]=i;}for(int i=n+1;i<=3*n+1;i++) val[i]=read()+1;//根據(jù)定義,val[u]應該+=(val[v]/2).(對于底層節(jié)點(其實適應于所有節(jié)點))考慮到轉(zhuǎn)換的方便用1和2表示.//1就代表對上一層沒貢獻,2就代表有貢獻.dfs(1);//算出初始的答案m=read();for(int i=1;i<=m;i++){int x=read();//實際上這里是一種卡常,如果split(x,y)就要makeroot極慢,所以改為這樣access(x);splay(x);if(val[x]>1){int y=findnot2(ch[x][0]); // Debug(fa[y]); fa[y]是原樹中的父親,不是splay中的.access(fa[y]);//這里的作用是把無關(guān)的點斷開,要access的理由是包括了pushup操作.splay(x);//access中使得根變了,所以要重新splay.modify(x,-1);}else{int y=findnot1(ch[x][0]);access(fa[y]);splay(x);modify(x,1);}splay(1);//要把1節(jié)點旋上來才能更新答案printf("%d\n",val[1]>>1);} }

3.2原始注釋

轉(zhuǎn)載于:https://www.cnblogs.com/lizehon/p/10462097.html

與50位技術(shù)專家面對面20年技術(shù)見證,附贈技術(shù)全景圖

總結(jié)

以上是生活随笔為你收集整理的P4332 [SHOI2014]三叉神经树(LCT)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。