这道题是HugeGun出的一道题(据说是他改编的,bzoj2333) 但原题很毒瘤啊,有负数,他说正解是堆套堆Orz。
作为什么也不会的蒟蒻,看到这个瞬间想到带权冰茶集(点权)。。。 (汪神说他写的是火茶集2333)
一开始开一个数组,表示这个点和他的子孙将要被加上的值(因为觉得像lazy思想,所以起名lazy)
对于修改所有结点,可以用一个变量来存取,输出时加上即可,对于修改单点,直接加在那个点上更新下当前所在集合的最大值,
对于修改集合,将值加在点所在集合的代表元的lazy上就行了。。。
合并两个集合时,假设这两个集合的代表元为x,y,那么lazy[x] 应减去 Lazy[y] 。 因为显然现在lazy[y]中的数跟x所在集合没有半毛钱关系,而x集合中的点在合并后会加上lazy[y],所以应在lazy[x]中减去lazy[y]....
That is all 。。。。。
#include<cstdlib> #include<cstdio> #include<algorithm> #include<string> #include<cstring> #include<cmath> #include<vector> #include<stack> const int N = 1e6+7; int n,Q,f[N]; char lz[10]; int w[N],laz[N],pre,all,maxl[N],suf; //马克思 int stk[N],top; int find (int x) { if(f[f[x]] == f[x]) return f[x]; int ret = x; top = 0; while(f[ret] != ret) stk[++top] = ret,ret = f[ret]; for(int i=top-1;i;--i) laz[stk[i]] += laz[stk[i+1]] , f[stk[i]] = ret; return ret; } void merge (int x,int y) { int l = find (x), r = find (y); if(l == r) return ; maxl[r] = std :: max (maxl[r],maxl[l]); f[l] = r; laz[l] -= laz[r]; } int main () { freopen("operation.in","r",stdin); freopen("operation.out","w",stdout); scanf("%d",&n); for(int i=1;i<=n;++i) scanf("%d",&w[i]) ,maxl[i] = w[i], f[i] = i, suf = std :: max(suf,w[i]); scanf("%d",&Q); while(Q--) { scanf("%s",lz); if(lz[0] == 'U') { int x,y; scanf("%d%d",&x,&y); merge((x+pre)%n+1,(y+pre)%n+1); } else if(lz[0] == 'A') { int x,y; scanf("%d",&x); if(lz[1] == '3') all += x; else { scanf("%d",&y); x = (x + pre) % n + 1; int q = find(x); if(lz[1] == '1') w[x] += y,maxl[q] = std :: max(maxl[q],w[x] + laz[x] + (x == q ? 0 : laz[q])) , suf = std :: max(maxl[q],suf); else laz[q] += y , maxl[q] += y, suf = std :: max(suf,maxl[q]); } } else if(lz[0] == 'F') { if(lz[1] == '3') { printf("%d ",pre = suf + all); continue; } int x; scanf("%d",&x); x = (x+pre)%n+1; int q = find (x); if(lz[1] == '1') { int an = w[x]; an += laz[x] + (x == q ? 0 : laz[q]); printf("%d ",pre = an + all); } else printf("%d ",pre = maxl[q] + all); } } //for(;;); return 0; }