BZOJ 3720 [洛谷P2137] : Gty的妹子树
Description
我曾在弦歌之中聽過你,
檀板聲碎,半出折子戲。
舞榭歌臺被風吹去,
歲月深處尚有余音一縷……
Gty神(xian)犇(chong)從來不缺妹子……
他來到了一棵妹子樹下,發現每個妹子有一個美麗度……
由于Gty很哲♂學,他只對美麗度大于某個值的妹子感興趣。
他想知道某個子樹中美麗度大于k的妹子個數。
某個妹子的美麗度可能發生變化……
樹上可能會出現一只新的妹子……
維護一棵初始有n個節點的有根樹(根節點為1),樹上節點編號為1-n,每個點有一個權值wi。
支持以下操作:
0 u x 詢問以u為根的子樹中,嚴格大于x的值的個數。(u^=lastans,x^=lastans)
1 u x 把u節點的權值改成x。(u^=lastans,x^=lastans)
2 u x 添加一個編號為"當前樹中節點數+1"的節點,其父節點為u,其權值為x。(u^=lastans,x^=lastans)
最開始時lastans=0。
Input
輸入第一行包括一個正整數n(1<=n<=30000),代表樹上的初始節點數。
接下來n-1行,每行2個整數u,v,為樹上的一條無向邊。
任何時刻,樹上的任何權值大于等于0,且兩兩不同。
接下來1行,包括n個整數wi,表示初始時每個節點的權值。
接下來1行,包括1個整數m(1<=m<=30000),表示操作總數。
接下來m行,每行包括三個整數 op,u,v:
op,u,v的含義見題目描述。
保證題目涉及的所有數在int內。
Output
對每個op=0,輸出一行,包括一個整數,意義見題目描述。
Sample Input
2
1 2
10 20
1
0 1 5
Sample Output
2
Solution
-
大家多用的是樹分塊的方法,這里我用的歸并樹+定期重構。
-
具體怎樣呢?關鍵是我們考慮每個修改對之后詢問的影響。
-
如果沒有修改(靜態詢問),我們對dfs序建歸并樹,直接區間查詢即可。
-
(歸并樹就是一種線段樹,區間內存的是這個區間權值排序后的序列,查詢時在上面二分)
-
有了修改,我們就要判斷修改對詢問有影響,其中修改點要在詢問點的子樹內。
-
如何判斷是否在子樹內:倍增跳。加點時處理出其 2i2^i2i 級父親。
-
于是我們得到這樣一個算法:我們在歸并樹中查詢后,針對若干修改操作暴力判斷影響。
-
那我們就可以定期重構啦!
-
如果我們在查詢之前的修改不超過 m\sqrt mm? 次時,就在歸并樹上查詢后暴力掃描修改計算貢獻;
-
如果修改超過了 m\sqrt mm? 次時,我們只要根據修改重建一下歸并樹就可以清除掉這些修改,
-
可以發現歸并樹的重建不會超過 m\sqrt mm? 次。
-
那么我們來分析一下復雜度:(假設 n,mn,mn,m 同階)
-
每次掃描修改算貢獻,修改最多 n\sqrt nn? 個,每次倍增判是否在子樹要 O(logn)O(log\ n)O(log?n) ,復雜度為 O(nnlogn)O(n\sqrt n\ log\ n)O(nn??log?n) 。
-
每次重建歸并樹要 O(nlogn)O(n\ log\ n)O(n?log?n) ,最多重建 O(logn)O(log\ n)O(log?n) 次,故復雜度同是 O(nnlogn)O(n\sqrt n\ log\ n)O(nn??log?n) 。
-
于是這題就解決了,總時間復雜度 O(nnlogn)O(n\sqrt n\ log\ n)O(nn??log?n) 。
-
有一些細節要注意:
-
由于重建歸并樹常數比較大,我們可以多幾次修改再重建一次,比如說 5?n5*\sqrt n5?n? 。
-
還有就是打線段樹詢問時:find(1,1,n)find(1,1,n)find(1,1,n) ,由于加點時 nnn 會增加,但帶進詢問時仍然要是之前的 nnn ,不然就不對了,重構時再把 find(1,1,n)find(1,1,n)find(1,1,n) 的 nnn 改成新的。
Code
#include<cstdio> #include<algorithm> #include<cmath> #include<vector> #include<cctype> using namespace std; const int N=60005; struct data {int op,x,y,z; }q[N>>1]; int n,tot,num,cnt,qx,qy,qz,last,lim; int first[N],nex[N<<1],en[N<<1]; int w[N],dfn[N],size[N],id[N],dep[N],fa[N][16]; vector<int>ss[N<<2]; inline int read() {int X=0,w=0; char ch=0;while(!isdigit(ch)) w|=ch=='-',ch=getchar();while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();return w?-X:X; } void write(int x) {if(x>9) write(x/10);putchar(x%10+'0'); } inline void insert(int x,int y) {nex[++tot]=first[x];first[x]=tot;en[tot]=y; } void dfs(int x) {dfn[x]=++cnt;id[cnt]=x;size[x]=1;dep[x]=dep[fa[x][0]]+1;for(int i=first[x];i;i=nex[i])if(en[i]^fa[x][0]){fa[en[i]][0]=x;dfs(en[i]);size[x]+=size[en[i]];} } void make(int v,int l,int r) {ss[v].clear();if(l==r){ss[v].push_back(w[id[l]]);return;}int mid=l+r>>1,ls=v<<1,rs=ls|1;make(ls,l,mid);make(rs,mid+1,r);int i=0,ni=ss[ls].size()-1;int j=0,nj=ss[rs].size()-1;while(i<=ni && j<=nj) ss[v].push_back(ss[ls][i]<ss[rs][j]?ss[ls][i++]:ss[rs][j++]);while(i<=ni) ss[v].push_back(ss[ls][i++]);while(j<=nj) ss[v].push_back(ss[rs][j++]); } int find(int v,int l,int r) {if(qx<=l && r<=qy) return ss[v].size()-(upper_bound(ss[v].begin(),ss[v].end()--,qz)-ss[v].begin());int mid=l+r>>1,s=0;if(qx<=mid) s=find(v<<1,l,mid);if(qy>mid) s+=find(v<<1|1,mid+1,r);return s; } void dfs1(int x) {size[x]=1;dfn[x]=++cnt;id[cnt]=x;for(int i=first[x];i;i=nex[i])if(en[i]^fa[x][0]){dfs(en[i]);size[x]+=size[en[i]];} } inline void rebuild() {cnt=num=0;dfs1(1);make(1,1,n);cnt=n; } inline bool belong(int x,int y) {if(dep[x]<dep[y]) return false;for(int i=log2(dep[x]);i>=0;i--)if(dep[fa[x][i]]>=dep[y]) x=fa[x][i];return x==y; } int main() {n=read();for(int i=1;i<n;i++){int x=read(),y=read();insert(x,y);insert(y,x);}for(int i=1;i<=n;i++) w[i]=read();dfs(1);cnt=n;for(int j=1;j<16;j++)for(int i=1;i<=n;i++)fa[i][j]=fa[fa[i][j-1]][j-1];make(1,1,n);int m=read();lim=ceil(sqrt(m)*5);while(m--){int op=read(),x=read()^last,y=read()^last;if(op==0){if(x<=cnt){qx=dfn[x],qy=dfn[x]+size[x]-1,qz=y;last=find(1,1,cnt);}else last=0;for(int i=1;i<=num;i++)if(q[i].op==1){if((q[i].y<y)^(q[i].z<y) && belong(q[i].x,x)) last+=q[i].z>y?1:-1;}else{if(q[i].z>y && belong(q[i].x,x)) last++;}write(last),putchar('\n');}elseif(op==1){q[++num]=(data){1,x,w[x],y};w[x]=y;if(num==lim) rebuild();}else{q[++num]=(data){2,++n,x,y};insert(x,n);insert(n,x);fa[n][0]=x;dep[n]=dep[x]+1;w[n]=y;for(int i=1;i<16;i++) fa[n][i]=fa[fa[n][i-1]][i-1];if(num==lim) rebuild();}}return 0; }總結
以上是生活随笔為你收集整理的BZOJ 3720 [洛谷P2137] : Gty的妹子树的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JZOJ 5941. 【NOIP2018
- 下一篇: JZOJ 5952. 【NOIP2018