bzoj2333 [SCOI2011]棘手的操作(洛谷3273)
题目描述
有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,输出对应的结果,每个结果占一行。
输入输出样例
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
做法
我的想法是 离线+线段树区间修改, 预处理出每个操作联通块在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