【BZOJ2333】棘手的操作(SCOI2011)-线段树+并查集+离线处理

测试地址:棘手的操作
做法:本题需要用到线段树+并查集+离线处理。
话说这题号还真喜庆……又据说这题原来是毒瘤数据结构,什么堆套左偏树之类……但是本蒟蒻从某大佬那里得到了离线做法的启示,所以就水了一发。
我们尝试构造一种点的排列方案,使得操作中涉及的所有连通块在排列中都是一个连续区间。这可以构造出来吗?当然可以!首先我们把每个点都看作一个区间,每当合并两个连通块,就是将两个区间放在一起,这个我们可以用链表维护。最后我们再把所有分散的连通块连起来,得到的链表上的点的顺序就是我们要求的排列了。
然后就是线段树区间修改区间询问的裸题了,同时用并查集维护一下当前的连通情况即可,时间复杂度是O(nlogn)
以下是本人代码:

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const ll inf=1000000000;
int n,m,first,nxt[300010],fa[300010],head[300010],tail[300010];
int pos[300010],q[300010];
ll a[300010],mx[1200010],p[1200010]={0},opx[300010],opy[300010];
char op[300010][4];

int find(int x)
{
    int r=x,i=x,j;
    while (r!=fa[r]) r=fa[r];
    while (i!=r)
    {
        j=fa[i];
        fa[i]=r;
        i=j;
    }
    return r;
}

void merge(int x,int y)
{
    int fx=find(x),fy=find(y);
    fa[fy]=fx;
    tail[fx]=tail[fy];
}

void pushdown(int no)
{
    if (p[no]!=0)
    {
        mx[no<<1]+=p[no],mx[no<<1|1]+=p[no];
        p[no<<1]+=p[no],p[no<<1|1]+=p[no];
        p[no]=0;
    }
}

void pushup(int no)
{
    mx[no]=max(mx[no<<1],mx[no<<1|1]);
}

void buildtree(int no,int l,int r)
{
    if (l==r)
    {
        mx[no]=a[q[l]];
        return;
    }
    int mid=(l+r)>>1;
    buildtree(no<<1,l,mid);
    buildtree(no<<1|1,mid+1,r);
    pushup(no);
}

void modify(int no,int l,int r,int s,int t,ll c)
{
    if (l>=s&&r<=t)
    {
        mx[no]+=c;
        p[no]+=c;
        return;
    }
    pushdown(no);
    int mid=(l+r)>>1;
    if (s<=mid) modify(no<<1,l,mid,s,t,c);
    if (t>mid) modify(no<<1|1,mid+1,r,s,t,c);
    pushup(no);
}

ll query(int no,int l,int r,int s,int t)
{
    if (l>=s&&r<=t) return mx[no];
    pushdown(no);
    int mid=(l+r)>>1;
    ll mxx=-inf*inf;
    if (s<=mid) mxx=max(mxx,query(no<<1,l,mid,s,t));
    if (t>mid) mxx=max(mxx,query(no<<1|1,mid+1,r,s,t));
    return mxx;
}

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%lld",&a[i]);

    scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%s",op[i]);
        if (op[i][0]=='U'||(op[i][0]=='A'&&op[i][1]<='2'))
            scanf("%lld%lld",&opx[i],&opy[i]);
        else if (op[i][0]=='A'||(op[i][0]=='F'&&op[i][1]<='2'))
            scanf("%lld",&opx[i]);
    }

    for(int i=1;i<=n;i++)
        fa[i]=tail[i]=i;
    for(int i=1;i<=m;i++)
        if (op[i][0]=='U'&&find(opx[i])!=find(opy[i]))
        {
            nxt[tail[find(opx[i])]]=find(opy[i]);
            merge(opx[i],opy[i]);
        }
    first=n+1;
    for(int i=1;i<n;i++)
        if (find(i)!=find(i+1))
        {
            nxt[tail[find(i)]]=find(i+1);
            first=min(first,find(i));
            merge(i,i+1);
        }
    if (first==n+1) first=find(1);

    for(int i=1,now=first;i<=n;i++,now=nxt[now])
    {
        pos[now]=i;
        q[i]=now;
    }

    buildtree(1,1,n);
    for(int i=1;i<=n;i++)
        fa[i]=i,tail[i]=pos[i];
    for(int i=1;i<=m;i++)
    {
        if (op[i][0]=='U'&&find(opx[i])!=find(opy[i]))
            merge(opx[i],opy[i]);
        if (op[i][0]=='A')
        {
            if (op[i][1]=='1') modify(1,1,n,pos[opx[i]],pos[opx[i]],opy[i]);
            if (op[i][1]=='2') modify(1,1,n,pos[find(opx[i])],tail[find(opx[i])],opy[i]);
            if (op[i][1]=='3') modify(1,1,n,1,n,opx[i]);
        }
        if (op[i][0]=='F')
        {
            ll ans;
            if (op[i][1]=='1') ans=query(1,1,n,pos[opx[i]],pos[opx[i]]);
            if (op[i][1]=='2') ans=query(1,1,n,pos[find(opx[i])],tail[find(opx[i])]);
            if (op[i][1]=='3') ans=query(1,1,n,1,n);
            printf("%lld\n",ans);
        }
    }

    return 0;
}
posted @ 2018-03-01 23:43  Maxwei_wzj  阅读(108)  评论(0编辑  收藏  举报