2333: [SCOI2011]棘手的操作
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 2881 Solved: 1117
[Submit][Status][Discuss]
Description
有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: 输出所有节点中,权值最大的节点的权值
Input
输入的第一行是一个整数N,代表节点个数。
接下来一行输入N个整数,a[1], a[2], …, a[N],代表N个节点的初始权值。
再下一行输入一个整数Q,代表接下来的操作数。
最后输入Q行,每行的格式如题目描述所示。
Output
对于操作F1, F2, F3,输出对应的结果,每个结果占一行。
Sample Input
3
0 0 0
8
A1 3 -20
A1 2 20
U 1 3
A2 1 10
F1 3
F2 3
A3 -10
F3
0 0 0
8
A1 3 -20
A1 2 20
U 1 3
A2 1 10
F1 3
F2 3
A3 -10
F3
Sample Output
-10
10
10
HINT
对于30%的数据,保证 N<=100,Q<=10000
对于80%的数据,保证 N<=100000,Q<=100000
对于100%的数据,保证 N<=300000,Q<=300000
对于所有的数据,保证输入合法,并且 -1000<=v, a[1], a[2], …, a[N]<=1000
Source
分析:挺好的一道题.
我一开始的想法是将点和连通块分开处理,最后合并询问就可以了.但是这样会出现一个问题:修改操作互相有影响,有时候修改了一个点可能还要修改连通块,统计点的答案可能还要统计连通块的答案等等,我写了一份这样的代码只能得10分QAQ
正确解法是利用链表+并查集+线段树.我们要做的事情实际上就是让需要修改查询的点的编号连续,也就是在线段树上能够一次修改完一个区间内所有的点.链表正好能够实现重编号的功能.并查集则是用来维护连通块的.
因为题目只涉及到加边操作,不会破坏图的结构,所以可以先把“U”操作给单独提出来做,对每一个连通块进行重编号.仅仅只是预先编号。然后将并查集和链表清空.在具体操作中动态加边.这样就能使得操作的点的序号在一个区间内是连续的了.接着就是正常的线段树操作了.在A3操作的时候有一个技巧:用一个变量sum记录加了多少,最后输出的时候加上sum就可以了.
易错点:建树和询问的重编号数组容易弄反,nextt数组是对于每一个连通块的最后一个元素而言的.
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int maxn = 300010,inf = 0x7ffffff; int n,a[maxn],tag[maxn << 2],maxx[maxn << 2],st[maxn],ed[maxn],pos[maxn],id[maxn],q; int fa[maxn],nextt[maxn],tot,sum; struct node { int id,x,y; } e[maxn]; int find(int x) { if (x == fa[x]) return x; return fa[x] = find(fa[x]); } void pushup(int o) { maxx[o] = max(maxx[o * 2],maxx[o * 2 + 1]); } void pushdown(int o) { if (tag[o]) { tag[o * 2] += tag[o]; tag[o * 2 + 1] += tag[o]; maxx[o * 2] += tag[o]; maxx[o * 2 + 1] += tag[o]; tag[o] = 0; } } void build(int o,int l,int r) { if (l == r) { maxx[o] = a[pos[l]]; return; } int mid = (l + r) >> 1; build(o * 2,l,mid); build(o * 2 + 1,mid + 1,r); pushup(o); } void update(int o,int l,int r,int x,int y,int v) { if (x <= l && r <= y) { tag[o] += v; maxx[o] += v; return; } pushdown(o); int mid = (l + r) >> 1; if (x <= mid) update(o * 2,l,mid,x,y,v); if (y > mid) update(o * 2 + 1,mid + 1,r,x,y,v); pushup(o); } int query(int o,int l,int r,int x,int y) { if (x <= l && r <= y) return maxx[o]; int mid = (l + r) >> 1,res = -1e9; pushdown(o); if (x <= mid) res = max(res,query(o * 2,l,mid,x,y)); if (y > mid) res = max(res,query(o * 2 + 1,mid + 1,r,x,y)); return res; } int main() { scanf("%d",&n); for (int i = 1; i <= n; i++) fa[i] = i,ed[i] = i; for (int i = 1; i <= n; i++) scanf("%d",&a[i]); scanf("%d",&q); for (int i = 1; i <= q; i++) { char ch[5]; int x,y; scanf("%s",ch); if (ch[0] == 'A' && ch[1] == '1') { e[i].id = 1; scanf("%d%d",&x,&y); e[i].x = x; e[i].y = y; } else if (ch[0] == 'A' && ch[1] == '2') { e[i].id = 2; scanf("%d%d",&x,&y); e[i].x = x; e[i].y = y; } else if (ch[0] == 'A' && ch[1] == '3') { e[i].id = 3; scanf("%d",&x); e[i].x = x; } else if (ch[0] == 'F' && ch[1] == '1') { e[i].id = 4; scanf("%d",&x); e[i].x = x; } else if (ch[0] == 'F' && ch[1] == '2') { e[i].id = 5; scanf("%d",&x); e[i].x = x; } else if (ch[0] == 'F' && ch[1] == '3') e[i].id = 6; else { e[i].id = 7; scanf("%d%d",&x,&y); e[i].x = x; e[i].y = y; } } for (int i = 1; i <= q; i++) { if (e[i].id == 7) { int fx = find(e[i].x),fy = find(e[i].y); nextt[ed[fx]] = fy; ed[fx] = ed[fy]; fa[fy] = fx; } } for (int i = 1; i <= n; i++) { if (i == find(i)) { int j = i; while (j != ed[i]) { pos[++tot] = j; id[j] = tot; j = nextt[j]; } pos[++tot] = j; id[j] = tot; } } build(1,1,n); for (int i = 1; i <= n; i++) fa[i] = i,ed[i] = i; for (int i = 1; i <= q; i++) { int x = e[i].x,y = e[i].y; if (e[i].id == 1) update(1,1,n,id[x],id[x],y); if (e[i].id == 2) update(1,1,n,id[find(x)],id[ed[find(x)]],y); if (e[i].id == 3) sum += x; if (e[i].id == 4) printf("%d ",query(1,1,n,id[x],id[x]) + sum); if (e[i].id == 5) printf("%d ",query(1,1,n,id[find(x)],id[ed[find(x)]]) + sum); if (e[i].id == 6) printf("%d ",maxx[1] + sum); if (e[i].id == 7) { int fx = find(x),fy = find(y); nextt[ed[fx]] = fy; ed[fx] = ed[fy]; fa[fy] = fx; } } return 0; }
我一开始的错解:
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; //单点修改,区间修改,单点查询,区间查询 const int maxn = 300010; int n,a[maxn],fa[maxn],maxx[maxn << 2],tag[maxn << 2],maxx2[maxn << 2],tag2[maxn << 2],q,sum[maxn << 2],L[maxn << 2],R[maxn << 2]; //sum主要看每个连通块加了多少次 int tag3[maxn << 2],all; void pushup(int o) { maxx[o] = max(maxx[o * 2],maxx[o * 2 + 1]); maxx2[o] = max(maxx2[o * 2],maxx2[o * 2 + 1]); sum[o] = sum[o * 2] + sum[o * 2 + 1]; } void build(int o,int l,int r) { L[o] = l,R[o] = r; if (l == r) { maxx[o] = maxx2[o] = a[l]; return; } int mid = (l + r) >> 1; build(o * 2,l,mid); build(o * 2 + 1,mid + 1,r); pushup(o); } int find(int x) { if (x == fa[x]) return x; return fa[x] = find(fa[x]); } void update1(int o,int l,int r,int x,int y) { if (l == r) { maxx[o] += y; return; } int mid = (l + r) >> 1; if (x <= mid) update1(o * 2,l,mid,x,y); else update1(o * 2 + 1,mid + 1,r,x,y); pushup(o); } void update2(int o,int l,int r,int x,int y) { if (l == r) { sum[o] += y; maxx2[o] += y; return; } int mid = (l + r) >> 1; if (x <= mid) update2(o * 2,l,mid,x,y); else update2(o * 2 + 1,mid + 1,r,x,y); pushup(o); } void update3(int o,int l,int r,int x,int y) { if (l == r) { maxx2[o] += y; return; } int mid = (l + r) >> 1; if (x <= mid) update3(o * 2,l,mid,x,y); else update3(o * 2 + 1,mid + 1,r,x,y); pushup(o); } void update4(int o,int l,int r,int x,int y) { if (l == r) { maxx2[o] = y; return; } int mid = (l + r) >> 1; if (x <= mid) update4(o * 2,l,mid,x,y); else update4(o * 2 + 1,mid + 1,r,x,y); pushup(o); } int query1(int o,int l,int r,int x) { if (l == r) return maxx[o]; int mid = (l + r) >> 1; if (x <= mid) return query1(o * 2,l,mid,x); else return query1(o * 2 + 1,mid + 1,r,x); } int query2(int o,int l,int r,int x) { if (l == r) return sum[o]; int mid = (l + r) >> 1; if (x <= mid) return query2(o * 2,l,mid,x); else return query2(o * 2 + 1,mid + 1,r,x); } int query3(int o,int l,int r,int x) { if (l == r) return maxx2[o]; int mid = (l + r) >> 1; if (x <= mid) return query3(o * 2,l,mid,x); else return query3(o * 2 + 1,mid + 1,r,x); } int main() { //freopen("data.txt","r",stdin); //freopen("ans.txt","w",stdout); scanf("%d",&n); for (int i = 1; i <= n; i++) { scanf("%d",&a[i]); fa[i] = i; } build(1,1,n); scanf("%d",&q); while (q--) { char ch[5]; int x,y; scanf("%s",ch); if (ch[0] == 'U') { scanf("%d%d",&x,&y); int fx = find(x),fy = find(y); if (fx != fy) { int temp1 = query3(1,1,n,fx),temp2 = query3(1,1,n,fy); int temp = max(temp1,temp2); fa[fx] = fy; update4(1,1,n,find(x),temp); } } else if (ch[0] == 'A' && ch[1] == '1') { scanf("%d%d",&x,&y); update1(1,1,n,x,y); //单点修改最大值 int temp = query1(1,1,n,x) + query2(1,1,n,find(x)); if (temp > query3(1,1,n,find(x))) update4(1,1,n,find(x),temp); } else if (ch[0] == 'A' && ch[1] == '2') { scanf("%d%d",&x,&y); update2(1,1,n,find(x),y); //连通块修改最大值 } else if (ch[0] == 'A' && ch[1] == '3') { scanf("%d",&x); all += x; } else if (ch[0] == 'F' && ch[1] == '1') { scanf("%d",&x); int temp = query1(1,1,n,x) + query2(1,1,n,find(x)); //单点查询+区间求和 //printf("%d %d ",query1(1,1,n,x),query2(1,1,n,find(x))); printf("%d ",temp + all); } else if (ch[0] == 'F' && ch[1] == '2') { scanf("%d",&x); printf("%d ",query3(1,1,n,find(x)) + all); //单点查询连通块 } else if (ch[0] == 'F' && ch[1] == '3') printf("%d ",maxx2[1] + all); } return 0; }
听说还能用左偏树做?以后再回来填坑.