bzoj2333 [SCOI2011]棘手的操作

2333: [SCOI2011]棘手的操作

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 2881  Solved: 1117
[Submit][Status][Discuss]

Description

N个节点,标号从1N,这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

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\n",query(1,1,n,id[x],id[x]) + sum);
        if (e[i].id == 5)
            printf("%d\n",query(1,1,n,id[find(x)],id[ed[find(x)]]) + sum);
        if (e[i].id == 6)
            printf("%d\n",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\n",query1(1,1,n,x),query2(1,1,n,find(x)));
            printf("%d\n",temp + all);
        }
        else if (ch[0] == 'F' && ch[1] == '2')
        {
            scanf("%d",&x);
            printf("%d\n",query3(1,1,n,find(x)) + all); //单点查询连通块
        }
        else if (ch[0] == 'F' && ch[1] == '3')
            printf("%d\n",maxx2[1] + all);
    }

    return 0;
}

听说还能用左偏树做?以后再回来填坑.

posted @ 2018-02-05 22:56  zbtrs  阅读(227)  评论(0编辑  收藏  举报