UvaLive 5031 Graph and Queries 平衡树+并查集+离线处理+树合并

版权声明:若转载请附上原博客链接,谢谢! https://blog.csdn.net/Link_Ray/article/details/86513256

题意

给出一个无向图,每次有三个操作

操作 描述
D X 删除ID为X的边。输入保证每条边至多被删除一次
Q X k 计算与结点X连通的结点中(包括本身)第k大的权值,如果不存在,直接输出0
C X V 把结点X的权值改为V

题解

采用离线的方式,从后往前做,把删除边当做是添加边。用平衡树Treap维护这些操作,添加边相当于树合并,修改权值可以用删除+插入来完成,需要注意的是这里的修改权值应该改成之前的值,因为离线的方法是从后往前做的。判断是否在同一个连通集内用并查集维护即可。
新建每一颗Treap的时候一定要在新图建号之后再New Node,否则权值并不是最新的权值。

具体代码

#include <iostream>
#include <cstdio>
#include <assert.h>
using namespace std;
const int maxn = 2e4+5;

int n,m,weight[maxn],qry;
int cnt = 0;

long long ans;

// Treap
struct Node{
    int v;
    int s;
    int r;
    Node* ch[2];
    Node(int v): v(v) {
        ch[0] = ch[1] = NULL;
        r = rand(); 
        s = 1;
    }
    int cmp(int x) const {
        if(x == v) return -1;
        return x < v ? 0:1;
    }
    void maintain() {
        s = 1;
        if(ch[0] != NULL) s += ch[0]->s;
        if(ch[1] != NULL) s += ch[1]->s;
    }
};
Node* root[maxn];
// 旋转, d = 0左旋, d = 1右旋
void rotate(Node* &o, int d) {
    Node* k = o->ch[d^1];
    o->ch[d^1] = k->ch[d];
    k->ch[d] = o;
    o->maintain();
    k->maintain();
    o = k;
}
// 插入
void insert(Node* &o, int x) {
    if(o == NULL) {
        o = new Node(x);
        
    }
    else {
        int d = x<o->v ? 0:1;
        insert(o->ch[d],x);
        if(o->ch[d]->r > o->r)
            rotate(o,d^1);
    }
    o->maintain();
}
// 删除
void remove(Node* &o, int x) {
    int d = o->cmp(x);
    if(d == -1) {
        Node* u = o;
        if(o->ch[0] != NULL && o->ch[1] != NULL) {
            int d2 = o->ch[0]->r > o->ch[1]->r ? 1 : 0;
            rotate(o,d2);
            remove(o->ch[d2],x);
        }
        else {
            if(o->ch[0] != NULL) {
                o = o->ch[0];
            }
            else 
                o = o->ch[1];
            delete u;
        }
    }
    else {
        remove(o->ch[d],x);
    }
    if(o != NULL)
        o->maintain();
}
// 第k大的值
int kth(Node* o, int k) {
    if(o == NULL || k <= 0 || k > o->s) 
        return 0;
    int s = (o->ch[1] == NULL)?0:(o->ch[1]->s);
    if(k == s+1) return o->v;
    if(k < s+1)
        return kth(o->ch[1], k);
    else 
        return kth(o->ch[0], k-s-1);
}

// 合并Treap
void mergeTo(Node* &src, Node* &dest) {
    if(src->ch[0] != NULL) mergeTo(src->ch[0], dest);
    if(src->ch[1] != NULL) mergeTo(src->ch[1], dest);
    insert(dest, src->v);
    delete src;
    src = NULL;
}
void removeTree(Node* &x) {
    if(x->ch[0] != NULL) removeTree(x->ch[0]);
    if(x->ch[1] != NULL) removeTree(x->ch[1]);
    delete x;
    x = NULL;
}
//并查集
int fa[maxn];
void init(int x) {
    for(int i = 0; i <= x; ++i) {
        fa[i] = i;
        if(root[i] != NULL)
            removeTree(root[i]);
        root[i] = new Node(weight[i]);
    }
}
int find(int x) {
    return x == fa[x] ? x : fa[x] = find(fa[x]);
}


const int maxc = 5e5+5;
struct Edge{
    int u,v;
    bool flag;
}edge[maxn*3];
struct Command{
    char op;
    int x;
    int val;
} command[maxc];

//小的合并到大的上
void add_node(int i) {
    int u = find(edge[i].u), v = find(edge[i].v);
    if(u != v) {
        if(root[u]->s > root[v]->s) {
            fa[v] = u;
            mergeTo(root[v], root[u]);
        }
        else {
            fa[u] = v;
            mergeTo(root[u], root[v]);
        }
    }
}

void solve() {
    for(int i = 1; i <= m; ++i) {
        if(edge[i].flag)
            add_node(i);
    }
    for(int i = cnt; i >= 1; --i) {
        char op = command[i].op;
        if(op == 'D') {
            int x = command[i].x;
            edge[x].flag = true;
            add_node(x);
        }
        else if(op == 'Q') {
            qry++;
            int x = command[i].x;   
            int k = command[i].val;
            x = find(x);
            ans += kth(root[x], k);
            // cout << ans << endl;
        }
        else if(op == 'C') {
            int x = command[i].x;   
            int v = command[i].val;
            x = find(x);
            remove(root[x], weight[command[i].x]);
            insert(root[x], v);
            weight[command[i].x] = v;
        }
    }
}
int main() {
//    freopen("in.txt", "r", stdin);
    int kase = 1;
    while(scanf("%d%d", &n, &m)==2 && n) {
        ans = 0;
        qry = 0;
        for(int i = 1; i <= n; ++i) {
            scanf("%d", &weight[i]);
        }
        for(int i = 1; i <= m; ++i) {
            scanf("%d%d", &edge[i].u, &edge[i].v);
            edge[i].flag = true;
        }
        char op;
        cnt = 0;
        while(scanf(" %c", &op)) {
            if(op == 'E')
                break;
            int x, val;
            scanf("%d", &x);
            if(op != 'D')  {
                scanf("%d", &val);
                if(op == 'C') {
                    int p = weight[x];
                    weight[x] = val;
                    val = p;
                }
            }
            else 
                edge[x].flag = false;
            command[++cnt].op = op;
            command[cnt].x = x;
            command[cnt].val = val;
        }
        init(n);
        solve();
        printf("Case %d: %.6lf\n", kase++, ans/(double)qry);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Link_Ray/article/details/86513256
今日推荐