刷題總結——棘手的操作(bzoj2333)
題目:
題目背景
SCOI2011 DAY2 T1
題目描述
有 N 個節點,標號從 1 到 N ,這 N 個節點一開始相互不連通。第i個節點的初始權值為 a[i] ,接下來有如下一些操作:
U x y: 加一條邊,連接第 x 個節點和第 y 個節點
A1 x v: 將第 x 個節點的權值增加 v
A2 x v: 將第 x 個節點所在的連通塊的所有節點的權值都增加 v
A3 v: 將所有節點的權值都增加 v
F1 x: 輸出第 x 個節點當前的權值
F2 x: 輸出第 x 個節點所在的連通塊中,權值最大的節點的權值
F3: 輸出所有節點中,權值最大的節點的權值
輸入格式
輸入的第一行是一個整數 N ,代表節點個數。
接下來一行輸入 N 個整數,a[1], a[2], …, a[N],代表 N 個節點的初始權值。
再下一行輸入一個整數 Q ,代表接下來的操作數。
最後輸入 Q 行,每行的格式如題目描述所示。
輸出格式
對於操作 F1, F2, F3,輸出對應的結果,每個結果占一行。
樣例數據 1
輸入 [復制]
3
0 0 0
8
A1 3 -20
A1 2 20
U 1 3
A2 1 10
F1 3
F2 3
A3 -10
F3
輸出
-10
10
10
備註
【數據範圍】
對於 30% 的數據,保證 N<=100,Q<=10000
對於 80% 的數據,保證 N<=100000,Q<=100000
對於 100% 的數據,保證 N<=300000,Q<=300000
對於所有的數據,保證輸入合法,並且 -1000<=v, a[1], a[2], …, a[N]<=1000
題解:
這道題可以用可並堆或者線段樹來寫····
然而如果用可並堆得話得會刪除特定的點的操作··然而··我一直認為如果有刪除特定的點的操作還不如用splay···
然後我就打了2個小時的splay····最後寫不出來棄療了·····這道題用可並堆做出來的人是真正的勇士·····
也從這道題中發現了自己代碼能力的不足··一是粗心大意···二是不懂得簡化代碼··搞得寫到最後腦袋發蒙·····以後多看看那些寫得簡潔的人的代碼····
然後我就用線段樹來寫了··下面將線段樹的方法:
其實用線段樹寫的話最重要的是要維護一個dfs序···在我們進行離線操作後dfs出來的dfs序必須保證:任意時間所有在同一個並查集裏的點的dfs序是連續的···
怎麽做到呢?有兩點:
第一點:我們離線時侯的建邊的順序必須是和我們在離線後dfs時的順序是一致的
打個比方···題目中先是連1,2的邊··後連1,3的邊··那麽我們在dfs時候必須先走1,2的邊····
要做到這一點很簡單··考慮到臨接表加邊的順序與dfs時的順序是反的···因此我們每次不直接加邊··先將邊保存起來···最後將所有邊倒著加入即可······
第二點:dfs順序必須和加邊的順序一致
依然舉上面的例子···如果我們連了2,1的邊···那麽我們必須保證先dfs2這個點,但如果按正常順序dfs,我們會先枚舉到1這個點,如果1在連了21之後與其他點連了邊··直接dfs就會發生錯誤····
所以我們可以將1打個標記···在枚舉到1的時候直接略過它··這樣就可以保證會先dfs到2,再dfs到1···與1相連的其他點也會被dfs到····換句話說··如果連了一條邊AB,我們要給B打一個標記····保證dfs時先dfs到點A,再dfs到點B
解決了dfs序的順序後接下來就是線段樹的基礎操作了
代碼:
#include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<ctime> #include<cctype> #include<cstring> #include<string> #include<algorithm> using namespace std; const int N=3e5+5; const int inf=0x3f3f3f3f; struct node { int x,y,op; }q[N]; struct node1 { int from,to; }e[N]; int dfn[N],last[N],tree[N*5],father[N],tag[N*5],n,m,num[N],val[N],fst[N],go[N],nxt[N],tot,to,cnt,tol,pre[N]; bool jud[N]; inline void comb(int a,int b) { nxt[++tot]=fst[a],fst[a]=tot,go[tot]=b; } inline int getfa(int u) { if(father[u]==u) return u; else return father[u]=getfa(father[u]); } inline int R() { char c;int f=0,i=1; for(c=getchar();(c<‘0‘||c>‘9‘)&&c!=‘-‘;c=getchar()); if(c==‘-‘) c=getchar(),i=-1; for(;c<=‘9‘&&c>=‘0‘;c=getchar()) f=(f<<3)+(f<<1)+c-‘0‘; return f*i; } inline void dfs(int u) { last[u]=dfn[u]=++cnt; for(int e=fst[u];e;e=nxt[e]) dfs(go[e]); } inline void add(int k,int v) { tree[k]+=v;tag[k]+=v; } inline void pushdown(int k) { if(tag[k]) { add(k*2,tag[k]); add(k*2+1,tag[k]); tag[k]=0; } } inline void build(int k,int l,int r) { if(l==r) { tree[k]=val[l];return; } int mid=(l+r)/2; build(k*2,l,mid);build(k*2+1,mid+1,r); tree[k]=max(tree[k*2],tree[k*2+1]); return; } inline void modify(int k,int l,int r,int x,int y,int v) { if(l>=x&&r<=y) { add(k,v);return; } int mid=(l+r)/2; pushdown(k); if(x<=mid) modify(k*2,l,mid,x,y,v); if(y>mid) modify(k*2+1,mid+1,r,x,y,v); tree[k]=max(tree[k*2],tree[k*2+1]); } inline int query(int k,int l,int r,int x,int y) { if(l>=x&&r<=y) return tree[k]; int mid=(l+r)/2;int temp=-inf; pushdown(k); if(x<=mid) temp=max(temp,query(k*2,l,mid,x,y)); if(y>mid) temp=max(temp,query(k*2+1,mid+1,r,x,y)); return temp; } int main() { //freopen("a.in","r",stdin); //freopen("a.out","w",stdout); memset(tree,-inf,sizeof(tree)); n=R();for(int i=1;i<=n;i++) num[i]=R(),father[i]=i; m=R();char s[5];memset(jud,true,sizeof(jud)); for(int i=1;i<=m;i++) { scanf("%s",s); if(s[0]==‘U‘) { q[i].op=1;q[i].x=R(),q[i].y=R(); int a=getfa(q[i].x),b=getfa(q[i].y); if(a!=b) father[a]=b,jud[a]=false,e[++tol].from=b,e[tol].to=a; } else if(s[0]==‘A‘) { if(s[1]==‘1‘) q[i].op=2,q[i].x=R(),q[i].y=R(); if(s[1]==‘2‘) q[i].op=3,q[i].x=R(),q[i].y=R(); if(s[1]==‘3‘) q[i].op=4,q[i].x=R(); } else if(s[0]==‘F‘) { if(s[1]==‘1‘) q[i].op=5,q[i].x=R(); if(s[1]==‘2‘) q[i].op=6,q[i].x=R(); if(s[1]==‘3‘) q[i].op=7; } } for(int i=tol;i;i--) comb(e[i].from,e[i].to); for(int i=1;i<=n;i++) { father[i]=i; if(!dfn[i]&&jud[i]) dfs(i); } for(int i=1;i<=n;i++) pre[i]=dfn[i],val[dfn[i]]=num[i];build(1,1,n); for(int i=1;i<=m;i++) { if(q[i].op==1) { int a=getfa(q[i].x),b=getfa(q[i].y); if(a!=b) father[a]=b,dfn[b]=max(dfn[b],dfn[a]),last[b]=min(last[b],last[a]); } if(q[i].op==2) { int a=q[i].x,b=q[i].y; modify(1,1,n,pre[a],pre[a],b); } if(q[i].op==3) { int a=getfa(q[i].x),b=q[i].y; modify(1,1,n,last[a],dfn[a],b); } if(q[i].op==4) { int a=q[i].x; modify(1,1,n,1,n,a); } if(q[i].op==5) { int a=q[i].x; printf("%d\n",query(1,1,n,pre[a],pre[a])); } if(q[i].op==6) { int a=getfa(q[i].x); printf("%d\n",query(1,1,n,last[a],dfn[a])); } if(q[i].op==7) printf("%d\n",query(1,1,n,1,n)); } return 0; }
刷題總結——棘手的操作(bzoj2333)