【BZOJ】2333: [SCOI2011]棘手的操作
http://www.lydsy.com/JudgeOnline/problem.php?id=2333
题意:
有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, Q<=200000,-1000<=v, a[i]<=1000
#include <bits/stdc++.h> using namespace std; const int N=300015, Lim=N; struct node *null; struct node { node *c[2], *f; int s, tag, mx, w; void init(int _w=-(~0u>>2)) { c[0]=c[1]=f=null; s=1; tag=0; mx=w=_w; } void up() { if(this==null) return; s=c[0]->s+c[1]->s+1; mx=max(w, max(c[0]->mx, c[1]->mx)); } void upd(int add) { if(this==null) return; mx+=add; w+=add; tag+=add; } void down() { if(tag) c[0]->upd(tag), c[1]->upd(tag), tag=0; } bool d() { return f->c[1]==this; } void setc(node *x, int d) { c[d]=x; x->f=this; } }Po[Lim], *iT=Po, *p[N]; node *newnode(int w=-(~0u>>2)) { iT->init(w); return iT++; } void rot(node *x) { node *f=x->f; f->down(); x->down(); bool d=x->d(); if(f->f!=null) f->f->setc(x, f->d()); else x->f=f->f; f->setc(x->c[!d], d); x->setc(f, !d); f->up(); } void splay(node *x, node *goal) { if(x==null) return; while(x->f!=goal) if(x->f->f==goal) rot(x); else x->d()==x->f->d()?(rot(x->f), rot(x)):(rot(x), rot(x)); x->up(); } int getrank(node *x) { splay(x, null); return x->c[0]->s; } node *sel(int k, node *x) { int s=x->c[0]->s; if(s==k) return x; if(s<k) return sel(k-s-1, x->c[1]); return sel(k, x->c[0]); } node *sel(int k) { splay(&Po[1], null); return sel(k, &Po[1]); } node *getrange(int l, int r) { node *nl=sel(l-1), *nr=sel(r+1); splay(nl, null); splay(nr, nl); return nr; } node *getblc(node *x, int len) { int rk=getrank(x); return getrange(rk, rk+len-1); } int pf[N], sz[N], wsum, a[N], n; int find(int x) { return pf[x]==x?x:pf[x]=find(pf[x]); } void U(int x, int y) { int fx=find(x), fy=find(y); if(fx==fy) return; if(sz[fx]<sz[fy]) swap(x, y), swap(fx, fy); sz[fx]+=sz[fy]; pf[fy]=fx; node *yf=getblc(p[fy], sz[fy]), *ny=yf->c[0]; // printf("%d, %d\n", yf, ny); yf->c[0]=null; ny->f=null; splay(yf, null); splay(p[fx], null); splay(sel(p[fx]->c[0]->s+1), p[fx]); p[fx]->c[1]->setc(ny, 0); splay(ny, null); } void A1(int x, int v) { splay(p[x], null); p[x]->w+=v; p[x]->up(); } void A2(int x, int v) { x=find(x); node *y=getblc(p[x], sz[x]); y->c[0]->upd(v); splay(y->c[0], null); } void A3(int v) { wsum+=v; } int F1(int x) { splay(p[x], null); return p[x]->w; } int F2(int x) { int rt=find(x); node *y=getblc(p[rt], sz[rt]); return y->c[0]->mx; } int F3() { splay(&Po[1], null); return Po[1].mx; } node *build(int l, int r) { if(l>r) return null; int mid=(l+r)>>1; node *x=p[mid]=newnode(a[mid]), *nl=build(l, mid-1), *nr=build(mid+1, r); if(nl!=null) x->setc(nl, 0); if(nr!=null) x->setc(nr, 1); x->up(); return x; } void init() { null=iT++; null->init(); null->s=0; node *l=newnode(), *r=newnode(); l->setc(r, 1); r->setc(build(1, n), 0); r->up(); l->up(); //D(l); for(int i=1; i<=n; ++i) pf[i]=i, sz[i]=1; } int main() { scanf("%d", &n); for(int i=1; i<=n; ++i) scanf("%d", &a[i]); init(); int Q; scanf("%d", &Q); while(Q--) { char cs[5]; int x, v; scanf("%s", cs); if(cs[0]=='A') { if(cs[1]=='1') scanf("%d%d", &x, &v), A1(x, v); else if(cs[1]=='2') scanf("%d%d", &x, &v), A2(x, v); else scanf("%d", &v), A3(v); } else if(cs[0]=='U') scanf("%d%d", &x, &v), U(x, v); else { int ans; if(cs[1]=='1') scanf("%d", &x), ans=F1(x); else if(cs[1]=='2') scanf("%d", &x), ans=F2(x); else ans=F3(); printf("%d\n", ans+wsum); } //puts(""); //splay(&Po[1], null); //D(&Po[1]); } return 0; }
其实我是直接输入2333来做的233333333
想了一下发现可以用splay来做0.0
大概就是维护一个序列,然后同一连通块在一段连续的区间,然后区间修改就行辣
然后连通块大小用并查集维护一下就行辣
(然后看到题解是一堆可并堆是什么鬼。。。。可并堆是什么.......QAQ
(窝看了一下,妈呀你们这个左偏树查询和深度有关,居然没被卡!!!差评!!然后我的splay是单次查询是$O(logn)$的居然还被卡常熟!!!!!!
博客地址:www.cnblogs.com/iwtwiioi 本文为博主原创文章,未经博主允许不得转载。一经发现,必将追究法律责任。