洛谷 P4175: bzoj 1146: [CTSC2008]網路管理
令人抓狂的整體二分題。根本原因還是我太菜了。
在學校寫了一個下午寫得頭暈,回家裡重寫了一遍,一個小時就寫完了……不過還是太慢。
題目傳送門:洛谷P4175。
題意簡述:
一棵 \(n\) 個結點的樹,每個點有點權。
有 \(m\) 次操作,每個操作要麼是更改單點點權,要麼是查詢樹鏈上第 \(k\) 大點權。
題解:
樹套樹固然可以,但是整體二分也很好。
整體二分就是對於所有的詢問一起二分答案,在二分割槽間範圍內的查詢和修改一併下傳。
這題把整體二分基礎題的操作搬到了鏈上,但是實現方法並沒有太大不同。
初始點權看成增加點權,插入在所有操作的最前面即可。
更改點權可以看成刪除點權再增加點權,變成兩次修改即可。
這題整體二分要求第 \(k\) 大,考慮二分出的答案 \(mid\),將大於 \(mid\) 的修改轉成單點權值 \(\pm 1\),
而對於樹鏈查詢第 \(k\) 大,則轉化成鏈上權值之和是否等於 \(k\)。
寫整體二分題永遠要注意二分的條件,我的條件是,鏈上大於 \(mid\) 的點數小於 \(k\) 個則答案小於等於 \(mid\),否則答案大於 \(mid\)。
單點修改,樹鏈查詢要是還用樹剖就太naive了,套路轉化:
考慮每個節點維護到根的路徑上的資訊,那麼單點修改就變成子樹修改,鏈查就變成四個單點查了(需要求LCA)。
而子樹是一個區間,區間加法,單點查詢;再使用樹狀陣列差分技巧轉化成單點差分,區間字首和。
注意到還要求LCA,直接在DFS的時候用Tarjan處理就好了。
關於判斷無解:當然可以直接處理掉……不過這樣就必須求樹鏈長度了。
我的方法是,往權值裡面加一個-1,如果答案是-1,則真實答案應該是無解。
我的程式碼還離散化了權值,其實沒用……
其他噁心的地方就是整體二分基本功了,太弱了調了好久……注意迴圈變數是指向真實操作的下標的指標還是真實操作的下標,如果你寫結構體當我沒說。
#include<stdio.h> #include<string.h> #include<algorithm> using namespace std; const int MN=80005; const int MM=110005; const int MQ=140005; int n,m,q,w[MN],d[MQ],c; int o[MQ],a[MQ],b[MQ],p[MQ],lc[MQ],ans[MQ]; int eh[MN],qh[MN],nxt[MM*2],to[MM*2],tot; inline void ins(int*h,int x,int y){nxt[++tot]=h[x],to[tot]=y,h[x]=tot;} int ld[MN],rd[MN],faz[MN],dfc; int fa[MN];int ff(int x){return fa[x]?fa[x]=ff(fa[x]):x;} void dfs(int u,int f){ faz[u]=f,ld[u]=++dfc; for(int i=eh[u];i;i=nxt[i])if(to[i]!=f)dfs(to[i],u),fa[to[i]]=u; for(int i=qh[u];i;i=nxt[i])if(lc[to[i]])lc[to[i]]=ff(lc[to[i]]);else lc[to[i]]=u; rd[u]=dfc; } int B[MN]; inline void I(int i,int x){for(;i<=n;i+=i&-i)B[i]+=x;} inline int Q(int i){int a=0;for(;i;i-=i&-i)a+=B[i];return a;} int t[MQ]; void s(int l,int r,int L,int R){ if(l>r)return; if(L==R){for(int i=l;i<=r;++i)ans[p[i]]=L;return;} int m=L+R>>1,p1=l-1,p2=r+1; for(int j=l,i;j<=r;++j){ if(o[i=p[j]]>0){ int x=Q(ld[a[i]])+Q(ld[b[i]])-Q(ld[lc[i]])-Q(ld[faz[lc[i]]]); if(x<o[i])o[i]-=x,t[++p1]=i; else t[--p2]=i; } else if(b[i]>m){ I(ld[a[i]],o[i]?-1:1),I(rd[a[i]]+1,o[i]?1:-1); t[--p2]=i; } else t[++p1]=i; } for(int i=l;i<=r;++i)if(o[p[i]]<=0&&b[p[i]]>m)I(ld[a[p[i]]],o[p[i]]?1:-1),I(rd[a[p[i]]]+1,o[p[i]]?-1:1); reverse(t+p2,t+r+1),memcpy(p+l,t+l,r-l+1<<2); s(l,p1,L,m),s(p2,r,m+1,R); } int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;++i)scanf("%d",&w[i]),o[++q]=0,a[q]=i,b[q]=w[i],p[q]=q; for(int i=1,x,y;i<n;++i)scanf("%d%d",&x,&y),ins(eh,x,y),ins(eh,y,x); for(int i=1;i<=m;++i){ ++q,scanf("%d%d%d",&o[q],&a[q],&b[q]),p[q]=q; if(!o[q])o[++q]=-1,a[q]=a[q-1],b[q]=w[a[q-1]],p[q]=q,w[a[q-1]]=b[q-1]; } for(int i=1;i<=q;++i)if(o[i]>0)ins(qh,a[i],i),ins(qh,b[i],i);else d[++c]=b[i]; d[++c]=-1;sort(d+1,d+c+1);c=unique(d+1,d+c+1)-d-1; for(int i=1;i<=q;++i)if(o[i]<=0)b[i]=lower_bound(d+1,d+c+1,b[i])-d; dfs(1,0),s(1,q,1,c); for(int i=1;i<=q;++i)if(o[i]>0)ans[i]==1?puts("invalid request!"):printf("%d\n",d[ans[i]]); return 0; } // 20:08 - 21:03