hdu3726——treap,并查集

题意:给定一个无向图,每个节点都赋予一个权值,给出三种操作:

  1. 删除一条边。
  2. 询问节点x所在连通分量的第k大的权值。
  3. 修改节点x的权值为k。
输出所有询问的平均值。

可以想到为每一个连通分量建一颗treap,修改操作很好实现,先删后加就行。但是对于删除操作,如果把一个连通分量分成了两个,那就要先把分出去的全删了,再建一棵treap,时间复杂度和代码复杂度都很操蛋的。我们可以反过来想:先把最终的图的treap建好,然后再将其复原回去,并在该过程中执行询问,这样修改操作没变,删除操作就变成了把两个连通分量合并(如果该边的两个顶点属于不同的连通分量),对应在treap里就是把两颗树合并可以先把节点个数小的treap的每个节点插入到另外一个里。

另外,我给treap节点加了一个id域,指明对应图中的节点的id,方便在合并的时候同时更新并查集。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace std;

const int maxn = 20000 + 10;
struct node
{
    node * ch[2];
    int r, v, s, id;
    void pushup()
    {
        s = 1;
        if(ch[0] != NULL) s += ch[0]->s;
        if(ch[1] != NULL) s += ch[1]->s;
    }
};

node * root[maxn];
int pre[maxn], val[maxn];
int n, m;

struct pp
{
    int cas, x, k;
}que[600000];

struct pp1
{
    int u, v, flag;
}edge[60010];

int Find(int a)
{
    if(a != pre[a]) return pre[a] = Find(pre[a]);
    else return a;
}

inline void rotate(node * & o, int d) // d = 0 左旋 d = 1 右旋
{
    node * k = o->ch[d ^ 1]; o->ch[d ^ 1] = k->ch[d]; k->ch[d] = o; o = k; k->ch[d]->pushup(); k->pushup();
}

void insert(node * & o, int x, int id)
{
    if(o == NULL) { o = new node(); o->ch[0] = o->ch[1] = NULL; o->v = x; o->r = rand(); o->id = id;}
    else
    {
        int d = (x < o->v || (x == o->v && id < o->id)) ? 0 : 1;
        insert(o->ch[d], x, id);
        if(o->ch[d]->r > o->r) rotate(o, d ^ 1);
    }
    o->pushup();
}

void remove(node * & o, int x, int id)
{
    if(o == NULL) return;
    int d = ((x < o->v || (x == o->v && id < o->id)) ? 0 : 1);
    if(o->v == x && id == o->id)
    {
        node * tmp = o;
        if(o->ch[0] == NULL) { o = o->ch[1]; delete tmp; }
        else if(o ->ch[1] == NULL) { o = o->ch[0]; delete tmp; }
        else
        {
            int d2 = (o->ch[0]->r > o->ch[1]->r) ? 1 : 0;
            rotate(o, d2); remove(o->ch[d2], x, id);
        }
    }
    else remove(o->ch[d], x, id);
    if(o != NULL) o->pushup();
}

int findk(node * o, int k)
{
    int tmp = 0;
    if(o->ch[1] != NULL) { tmp += o->ch[1]->s; if(tmp >= k) return findk(o->ch[1], k); }
    tmp++; if(tmp == k) return o->v;
    if(o->ch[0] != NULL) { if(k > tmp) return findk(o->ch[0], k - tmp); }
    return 0;
}

void merge(int id, node * & a, node * & b)
{
    if(b->ch[0] != NULL) merge(id, a, b->ch[0]);
    if(b->ch[1] != NULL) merge(id, a, b->ch[1]);
    pre[b->id] = id; insert(a, b->v, b->id); delete b; b = NULL;
}

int main()
{
    freopen("in", "r", stdin);
    int cc = 1;
    while(~scanf("%d %d", &n, &m) && n && m)
    {
        for(int i = 1; i <= n; ++i) scanf("%d", val + i);
        for(int i = 1; i <= m; ++i)
        {
            scanf("%d %d", &edge[i].u, &edge[i].v);
            edge[i].flag = 1;
        }
        char s[10]; int c = 0;
        while(~scanf("%s", s) && s[0] != 'E')
        {
            c++;
            if(s[0] == 'D')
            {
                que[c].cas = 1;
                scanf("%d", &que[c].x);
                edge[que[c].x].flag = 0;
            }
            else if(s[0] == 'Q')
            {
                que[c].cas = 2;
                scanf("%d %d", &que[c].x, &que[c].k);
            }
            else
            {
                que[c].cas = 3;
                scanf("%d", &que[c].x);
                que[c].k = val[que[c].x];
                scanf("%d", &val[que[c].x]);
            }
        }
        printf("Case %d: ", cc++);
        for(int i = 1; i <= n; ++i) { root[i] = NULL; insert(root[i], val[i], i); pre[i] = i; }
        for(int i = 1; i <= m; ++i)
        {
            if(edge[i].flag != 0)
            {
                int tmpa = Find(edge[i].u), tmpb = Find(edge[i].v);
                if(tmpa == tmpb) continue;
                if(root[tmpa]->s > root[tmpb]->s) merge(tmpa, root[tmpa], root[tmpb]);
                else merge(tmpb, root[tmpb], root[tmpa]);
            }
        }
        double res = 0, nn = 0;
        for(int i = c; i > 0; --i)
        {
            if(que[i].cas == 1)
            {
                int tmpa = Find(edge[que[i].x].u), tmpb = Find(edge[que[i].x].v);
                if(tmpa == tmpb) continue;
                if(root[tmpa]->s > root[tmpb]->s) merge(tmpa, root[tmpa], root[tmpb]);
                else merge(tmpb, root[tmpb], root[tmpa]);
            }
            else if(que[i].cas == 2)
            {
                nn++;
                res += findk(root[Find(que[i].x)], que[i].k);
            }
            else
            {
                int tmp = Find(que[i].x);
                remove(root[tmp], val[que[i].x], que[i].x);
                insert(root[tmp], que[i].k, que[i].x);
                val[que[i].x] = que[i].k;
            }
        }
        if(nn != 0) printf("%.6lf\n", res / nn);
        else printf("0\n");
    }
    return 0;
}
/*
3 3
10
20
30
1 2
2 3
1 3
D 3
Q 1 2
Q 2 1
D 2
Q 3 2
C 1 50
Q 1 1
E

3 3
10
20
20
1 2
2 3
1 3
Q 1 1
Q 1 2
Q 1 3
E

0 0


Case 1: 25.000000
Case 2: 16.666667
*/


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值