1. 程式人生 > >bzoj2333 [SCOI2011]棘手的操作(洛谷3273)

bzoj2333 [SCOI2011]棘手的操作(洛谷3273)

copy 節點 std 一個 線段樹 pan con amp fin

題目描述

有N個節點,標號從1到N,這N個節點一開始相互不連通。第i個節點的初始權值為a[i],接下來有如下一些操作:U x y: 加一條邊,連接第x個節點和第y個節點A1 x v: 將第x個節點的權值增加vA2 x v: 將第x個節點所在的連通塊的所有節點的權值都增加vA3 v: 將所有節點的權值都增加vF1 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
輸出樣例#1:
-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

做法

我的想法是 離線+線段樹區間修改, 預處理出每個操作聯通塊在DFS上的區間,然後上線段樹就好了。

連邊的時候,連的是這個點 並查集的祖先 ,而不是這個點,這樣才能讓所有相應聯通塊在連續的區間裏(不理解就畫一畫)。

連邊同時順便維護每個聯通塊對應區間的L,R。

接著就線段樹,

正經吐槽

細節很多。

由於是有負數的,一定在建立線段樹時把值都賦成-INF,還有查詢越界的return -INF

LazyTag……

預處理時並查集是找祖先連邊用的,預處理完要把並查集清空(復原)。

別的細節我寫在代碼註釋裏面好了

代碼

#include<bits/stdc++.h>
#define
MAXN 300005 #define INF 0x7f7f7f7f using namespace std; int read(){ int x=0,t=1;char c=getchar(); while(c<0||c>9){if(c==-)t=-1;c=getchar();} while(c>=0&&c<=9){x=x*10+c-0;c=getchar();} return x*t; } int N,Q,a[MAXN],father[MAXN],cnt1,lazy[MAXN<<2],X[MAXN],Y[MAXN],cnt2,cont,pos[MAXN],dfso[MAXN],vis[MAXN],L[MAXN],R[MAXN],last[MAXN]; char s[5]; int find(int x){return father[x]==x?x:father[x]=find(father[x]);} struct Node{int l,r,Max;}tree[MAXN<<2]; struct Edge{int other,pre;}e[MAXN]; struct Operation{int type,x,y;}q[MAXN]; void connect(int x,int y){ if(x==y)return; e[++cnt2]=(Edge){y,last[x]}; last[x]=cnt2; } void DFS(int x){ cont++;L[x]=R[x]=pos[x]=cont,dfso[cont]=x;              //dfso是dfs序數組,pos數組是點對應的dfs序位置 for(int i=last[x];i;i=e[i].pre)DFS(e[i].other); } void Pushdown(int k){ if(!lazy[k])return; tree[k<<1].Max+=lazy[k],lazy[k<<1]+=lazy[k]; tree[k<<1|1].Max+=lazy[k],lazy[k<<1|1]+=lazy[k]; lazy[k]=0; } void Build_tree(int k,int l,int r){ tree[k].Max=-INF;                            //初值賦成-INF tree[k].l=l,tree[k].r=r; int mid=l+r>>1; if(l==r){tree[k].Max=a[dfso[l]];return;} Build_tree(k<<1,l,mid); Build_tree(k<<1|1,mid+1,r); tree[k].Max=max(tree[k<<1].Max,tree[k<<1|1].Max); } void Modify(int k,int l,int r,int x){ if(tree[k].l>r||tree[k].r<l)return; if(l<=tree[k].l&&tree[k].r<=r){tree[k].Max+=x,lazy[k]+=x;return ;} Pushdown(k); Modify(k<<1,l,r,x); Modify(k<<1|1,l,r,x); tree[k].Max=max(tree[k<<1].Max,tree[k<<1|1].Max); } int Query(int k,int l,int r){ if(tree[k].l>r||tree[k].r<l)return -INF;                //越界返回-INF if(l<=tree[k].l&&tree[k].r<=r)return tree[k].Max; Pushdown(k); return max( Query(k<<1,l,r),Query(k<<1|1,l,r) ); } int main() { N=read(); for(int i=1;i<=N;i++)a[i]=read(),father[i]=i; Q=read(); for(int i=1;i<=Q;i++){ scanf("%s",s); if(s[0]==U){ q[i].type=1; int x,y; q[i].x=x=read(),q[i].y=y=read(); int fx=find(x), fy=find(y); cnt1++; father[fy]=fx; X[cnt1]=fx, Y[cnt1]=fy;                //存一下要連的邊,是連的祖先! } if(s[0]==A){ if(s[1]==1)q[i].type=2,q[i].x=read(),q[i].y=read(); //x點+v if(s[1]==2)q[i].type=3,q[i].x=read(),q[i].y=read(); //x點聯通塊+v if(s[1]==3)q[i].type=4,q[i].x=read(); //全體+v } if(s[0]==F){ if(s[1]==1)q[i].type=5,q[i].x=read(); //詢問x點 if(s[1]==2)q[i].type=6,q[i].x=read(); //詢問x點所在塊 if(s[1]==3)q[i].type=7; //全體詢問 } } for(int i=cnt1;i>0;i--)connect(X[i],Y[i]); for(int i=1;i<=N;i++)if(!vis[find(i)]){ DFS(find(i));vis[find(i)]=1;                     //跑DFS序 } for(int i=1;i<=N;i++)father[i]=i;                    //復原並查集 Build_tree(1,1,N); for(int i=1;i<=Q;i++){ if(q[i].type==1){ int fx=find(q[i].x),fy=find(q[i].y); L[fx]=min(L[fx],L[fy]), R[fx]=max(R[fx],R[fy]);    //合並時維護聯通塊對應區間的L,R father[fy]=fx; }
     //簡單的線段樹操作
if(q[i].type==2)Modify(1,pos[q[i].x],pos[q[i].x],q[i].y); if(q[i].type==3){int fx=find(q[i].x);Modify(1,L[fx],R[fx],q[i].y);} if(q[i].type==4)Modify(1,1,N,q[i].x); if(q[i].type==5)printf("%d\n",Query(1,pos[q[i].x],pos[q[i].x])); if(q[i].type==6){int fx=find(q[i].x);printf("%d\n",Query(1,L[fx],R[fx]));} if(q[i].type==7)printf("%d\n",Query(1,1,N)); } return 0; }
//haha

不正經吐槽

我是周六上午上課時寫寫畫畫 思考著這個題如何離線,下午和dalao們說了這個題 他們就去寫在線的線段樹合並了。

一遍寫的比較順利。

接著我就開始調錯。

我沒賦初值-INF         怎麽改都是0

主函數裏沒有調用 Build_tree   怎麽改都是-INF

lazytag沒有判斷        怎麽改都WrongAnswer

最後一次 發現錯在了 ↓

void Pushdown(int k){
    if(!lazy[k])return;
    tree[k<<1].Max+=k,lazy[k<<1]+=k;
    tree[k<<1|1].Max+=k,lazy[k<<1|1]+=k;
    lazy[k]=0;
}

把k當成了lazy[k] ,還看不出來 , 結果一Pushdown出現了一些本來就沒有的數。弱智。

我開始質疑我的智商水平了

推送

http://music.163.com/#/song/524164335/?userid=476005944

尋夢環遊記 - Remember Me-泠鳶

歌手:泠鳶yousa

bzoj2333 [SCOI2011]棘手的操作(洛谷3273)