[BZOJ2333][SCOI2011]棘手的操作(可并堆||线段树+离线)

来源:互联网 发布:知乎 林存德 编辑:程序博客网 时间:2024/06/09 22:38

题目描述

传送门

题解

对没错总会有一道题分到这个编号…233333

第一眼想到线段树,处理生成树(重新编号)使在任何时间在同一个连通块里的点都在一个连续的区间,然后搞搞搞就可以了
然而要是换成可并堆的话操作还真是棘手啊…

首先我们要维护两种可并堆(一个可并堆和一个splay也可以…),以下简称a堆和b堆
a堆是按照题目中的操作合并的,也就是说有若干个小堆
b堆只有一个堆,维护的是所有a堆的堆顶(最大值),也就是说堆中的节点数等于a堆的个数
a中每一个堆顶要维护一个标记add,表示当前这个堆中键值+add为实际值
维护全局ADD,表示总体加的值

U x y :在a中找到x和y中的根fx和fy,在b中删除fx和fy;在a中合并x和y,得到新的根top;将top插入b
A1 x v :在a中找到x的根fx,在a中删除x,在b中删除fx;修改x的键值,将x插入a,得到新根top;将top插入b
A2 x v :在a中找到x的根fx,在b中删除fx;在a中修改fx的标记,在b中修改fx的键值;在b中插入fx
A3 x :ADD+=v
F1 x :在a中找到x的根fx,key(x)+add(fx)+ADD
F2 x :在a中找到x的根fx,key(fx)+add(fx)+ADD
F3 x :找到b的根fx,key(fx)+ADD

注意:
a中每个根有标记add,堆中的键值+堆顶add才是正确值;b中没有标记,维护的键值即为实际值
U中将a堆合并时由于堆顶标记不同,需要修改某一个堆中的键值,使两堆标记相同。用启发式合并,每次暴力修改较小的堆,每个点只会被合并至多logn

时间复杂度O(nlogn)*一坨常数

写完可并堆了之后发现线段树的写法之前从来没写过又去写了一发…
值得一做的题.

代码

可并堆

#include<algorithm>#include<iostream>#include<cstring>#include<cstdio>#include<cmath>#include<queue>using namespace std;#define N 300005int n,m,x,y,fx,fy,top,tmp,temp,v,ADD,lastsize,lastadd;int ls[N],rs[N],dis[N],key[N],f[N],add[N],par[N],size[N];char opt[5];queue <int> q;void Add(int x,int v)//启发式 {    if (ls[x]) Add(ls[x],v);    if (rs[x]) Add(rs[x],v);    key[x]+=v;    return;}int find(int x)//找根 {    if (x==f[x]) return x;    f[x]=find(f[x]);    return f[x];}int merge(int x,int y)//合并 {    if (!x) return y;    if (!y) return x;    if (key[x]<key[y]) swap(x,y);    rs[x]=merge(rs[x],y);    par[rs[x]]=x;    if (dis[ls[x]]<dis[rs[x]]) swap(ls[x],rs[x]);    if (!rs[x]) dis[x]=0;    else dis[x]=dis[rs[x]]+1;    return x;}int del(int x)//删除x,返回x原先所在堆中的根(若删除x之后堆为空,则返回0) {    int p,q,re;    q=par[x];    p=merge(ls[x],rs[x]);    if (p) par[p]=q;    if (q&&ls[q]==x) ls[q]=p;    if (q&&rs[q]==x) rs[q]=p;    if (!q) re=p;    else re=find(q);    while (q)    {        if (dis[ls[q]]<dis[rs[q]])            swap(ls[q],rs[q]);        if (dis[rs[q]]+1==dis[q])            break;        dis[q]=dis[rs[q]]+1;        q=par[q];    }    ls[x]=rs[x]=dis[x]=par[x]=0;    return re;}namespace st{    int ls[N],rs[N],dis[N],key[N],par[N];    int root;    int merge(int x,int y)//合并     {        if (!x) return y;        if (!y) return x;        if (key[x]<key[y]) swap(x,y);        rs[x]=merge(rs[x],y);        par[rs[x]]=x;        if (dis[ls[x]]<dis[rs[x]]) swap(ls[x],rs[x]);        if (!rs[x]) dis[x]=0;        else dis[x]=dis[rs[x]]+1;        return x;    }    void clear()//左偏树构建     {        for (int i=1;i<=n;++i) q.push(i);        while (!q.empty())        {            x=q.front();q.pop();            if (!q.empty()) {y=q.front();q.pop();}            else break;            top=merge(x,y);            q.push(top);        }        st::root=x;    }    int del(int x)//删除x,返回根(可能为0)     {        int p,q;        q=par[x];        p=merge(ls[x],rs[x]);        if (p) par[p]=q;        if (q&&ls[q]==x) ls[q]=p;        if (q&&rs[q]==x) rs[q]=p;        if (!q) root=p;        while (q)        {            if (dis[ls[q]]<dis[rs[q]])                swap(ls[q],rs[q]);            if (dis[q]==dis[rs[q]]+1)                break;            dis[q]=dis[rs[q]]+1;            p=q;            q=par[q];        }        ls[x]=rs[x]=dis[x]=par[x]=0;        f[x]=x;        return root;    }}int main(){    scanf("%d",&n);    for (int i=1;i<=n;++i)    {        scanf("%d",&key[i]);        st::key[i]=key[i];    }    for (int i=1;i<=n;++i) f[i]=i,size[i]=1;    dis[0]=st::dis[0]=-1;    st::clear();    scanf("%d",&m);    for (int i=1;i<=m;++i)    {        scanf("%s",opt);        if (opt[0]=='U')        {            scanf("%d%d",&x,&y);            fx=find(x),fy=find(y);            if (fx!=fy)            {                tmp=st::del(fx);                tmp=st::del(fy);                if (size[fx]>size[fy])                    swap(x,y),swap(fx,fy);                Add(fx,add[fx]-add[fy]);                top=merge(fx,fy);                if (fx) f[fx]=top;                if (fy) f[fy]=top;                size[top]=size[fx]+size[fy];                add[top]=add[fy];                temp=st::merge(tmp,top);                st::root=temp;            }        }        else if (opt[0]=='A')        {            if (opt[1]=='1')            {                scanf("%d%d",&x,&v);                fx=find(x);                tmp=st::del(fx);                lastsize=size[fx];                lastadd=add[fx];                y=del(x);                key[x]+=v;                top=merge(x,y);                if (x) f[x]=top;                if (y) f[y]=top;                size[top]=lastsize;                add[top]=lastadd;                st::key[top]=key[top]+add[top];                temp=st::merge(tmp,top);                st::root=temp;            }            else if (opt[1]=='2')            {                scanf("%d%d",&x,&v);                fx=find(x);                tmp=st::del(fx);                add[fx]+=v;                st::key[fx]=key[fx]+add[fx];                temp=st::merge(tmp,fx);                st::root=temp;            }            else if (opt[1]=='3')            {                scanf("%d",&v);                ADD+=v;            }        }        else if (opt[0]=='F')        {            if (opt[1]=='1')            {                scanf("%d",&x);                fx=find(x);                printf("%d\n",key[x]+add[fx]+ADD);            }            else if (opt[1]=='2')            {                scanf("%d",&x);                fx=find(x);                printf("%d\n",key[fx]+add[fx]+ADD);            }            else if (opt[1]=='3')            {                x=st::root;                printf("%d\n",st::key[x]+ADD);            }        }    }    return 0;}

线段树+离线

#include<algorithm>#include<iostream>#include<cstring>#include<cstdio>#include<cmath>#include<queue>using namespace std;#define N 300005#define inf 2100000000int n,m,fx,fy,cnt,ans;char opt[N][3];int a[N],x[N],y[N],v[N],f[N],size[N],from[N],to[N],l[N],r[N],pt[N],val[N];int maxn[N*4],delta[N*4];int find(int x){    if (x==f[x]) return x;    f[x]=find(f[x]);    return f[x];}void update(int now){    maxn[now]=max(maxn[now<<1],maxn[now<<1|1]);}void pushdown(int now,int l,int r,int mid){    if (delta[now])    {        maxn[now<<1]+=delta[now];delta[now<<1]+=delta[now];        maxn[now<<1|1]+=delta[now];delta[now<<1|1]+=delta[now];        delta[now]=0;    }}void build(int now,int l,int r){    int mid=(l+r)>>1;    if (l==r)    {        maxn[now]=val[l];        return;    }    build(now<<1,l,mid);    build(now<<1|1,mid+1,r);    update(now);}void change(int now,int l,int r,int lrange,int rrange,int v){    int mid=(l+r)>>1;    if (lrange<=l&&r<=rrange)    {        maxn[now]+=v;        delta[now]+=v;        return;    }    pushdown(now,l,r,mid);    if (lrange<=mid) change(now<<1,l,mid,lrange,rrange,v);    if (mid+1<=rrange) change(now<<1|1,mid+1,r,lrange,rrange,v);    update(now);}int query(int now,int l,int r,int lrange,int rrange){    int mid=(l+r)>>1,ans=-inf;    if (lrange<=l&&r<=rrange) return maxn[now];    pushdown(now,l,r,mid);    if (lrange<=mid) ans=max(ans,query(now<<1,l,mid,lrange,rrange));    if (mid+1<=rrange) ans=max(ans,query(now<<1|1,mid+1,r,lrange,rrange));    return ans;}int main(){    scanf("%d",&n);    for (int i=1;i<=n;++i) scanf("%d",&a[i]);    for (int i=1;i<=n;++i) f[i]=i,size[i]=1;    scanf("%d",&m);    for (int i=1;i<=m;++i)    {        scanf("%s",opt[i]);        if (opt[i][0]=='U')        {            scanf("%d%d",&x[i],&y[i]);            fx=find(x[i]),fy=find(y[i]);            if (fx==fy) continue;            if (fx<fy) swap(x[i],y[i]),swap(fx,fy);            size[fy]+=size[fx];            f[fx]=fy;            from[++cnt]=fx;to[cnt]=fy;        }        else if (opt[i][0]=='A')        {            if (opt[i][1]=='1')                scanf("%d%d",&x[i],&v[i]);            else if (opt[i][1]=='2')                scanf("%d%d",&x[i],&v[i]);            else if (opt[i][1]=='3')                scanf("%d",&v[i]);        }        else if (opt[i][0]=='F')        {            if (opt[i][1]=='1')                scanf("%d",&x[i]);            else if (opt[i][1]=='2')                scanf("%d",&x[i]);        }    }    int last=0;    for (int i=1;i<=n;++i)        if (find(i)==i&&size[i]>1)        {            l[i]=last+1;            r[i]=pt[i]=last+size[i];            last+=size[i];        }    for (int i=cnt;i>=1;--i)    {        r[from[i]]=pt[from[i]]=pt[to[i]];        l[from[i]]=r[from[i]]-size[from[i]]+1;        pt[to[i]]=l[from[i]]-1;    }    for (int i=1;i<=n;++i)        if (find(i)==i&&size[i]==1)            l[i]=r[i]=++last;    for (int i=1;i<=n;++i) f[i]=i,size[i]=1,val[l[i]]=a[i];    build(1,1,n);    for (int i=1;i<=m;++i)    {        if (opt[i][0]=='U')        {            fx=find(x[i]),fy=find(y[i]);            if (fx==fy) continue;            if (fx<fy) swap(fx,fy),swap(x[i],y[i]);            f[fx]=fy;            size[fy]+=size[fx];        }        else if (opt[i][0]=='A')        {            if (opt[i][1]=='1')                change(1,1,n,l[x[i]],l[x[i]],v[i]);            else if (opt[i][1]=='2')            {                fx=find(x[i]);                change(1,1,n,l[fx],l[fx]+size[fx]-1,v[i]);            }            else if (opt[i][1]=='3')                change(1,1,n,1,n,v[i]);        }        else if (opt[i][0]=='F')        {            if (opt[i][1]=='1')            {                ans=query(1,1,n,l[x[i]],l[x[i]]);                printf("%d\n",ans);            }            else if (opt[i][1]=='2')            {                fx=find(x[i]);                ans=query(1,1,n,l[fx],l[fx]+size[fx]-1);                printf("%d\n",ans);            }            else if (opt[i][1]=='3')            {                ans=query(1,1,n,1,n);                printf("%d\n",ans);            }        }    }    return 0;}
0 0
原创粉丝点击