HDU 3726 Graph and Queries 离线处理 treap + 并查集

11 篇文章 0 订阅
6 篇文章 0 订阅

题目链接

题意

给定一个图,每个点上有权值。三种操作:
1. 删去某条边;
2. 修改某个点的权值;
3. 询问与某个点连通的所有点中权值第 k 大的值;
最后输出所有询问的平均值。

思路

因为是离线操作,所以考虑 倒着处理,先删去所有要删的边,倒着处理的时候再加回去,用并查集维护。
每一个集合都是一个 Treap,合并的时候把 size 小的树里面的点一个个拆出来往 size 大的里面加(即启发式合并)。

Code

#include <bits/stdc++.h>
#define maxn 60010
#define maxm 60010
#define maxop 500010
using namespace std;
typedef long long LL;
int val[maxn], fa[maxn], sz[maxn], u[maxn], v[maxn], n, m, tot, ne[maxn], ccc;
bool vis[maxn], flag[maxn];
struct Edge {
    int to, ne;
    Edge(int a = 0, int b = 0) : to(a), ne(b) {}
}edge[maxm * 2];
struct Oper { int t, x, y; Oper(int _t=0, int _a=0, int _b=0) : t(_t), x(_a), y(_b) {}} oper[maxop];
struct node {
    node* ch[2];
    int sz, key, val;
    node() { sz = 0; key = INT_MAX; }
    node(int x);
    update() { sz = ch[0]->sz+ch[1]->sz+1; }
}*null = new node;
node::node(int x) {
    sz = 1, key = rand(), val = x;
    ch[0] = ch[1] = null;
}
struct treap {
    node* root;
    treap() { root = null; }
    void rotate(node*& t, bool d) {
        node* p = t->ch[d];
        t->ch[d] = p->ch[!d];
        p->ch[!d] = t;
        t->update(), p->update();
        t = p;
    }
    void insert(node*& t, int x) {
        if (t == null) {
            t = new node(x);
            return;
        }
        bool d = x > t->val;
        insert(t->ch[d], x);
        if (t->ch[d]->key < t->key) rotate(t, d);
        else t->update();
    }
    void erase(node*& t, int x) {
        if (t == null) return;
        if (t->val == x) {
            bool d = t->ch[1]->key < t->ch[0]->key;
            if (t->ch[d] == null) { delete t; t = null; return; }
            if (t->ch[!d] == null) { node* p = t->ch[d]; delete t; t = p; return; }
            rotate(t, d);
            erase(t->ch[!d], x);
            t->update();
            return;
        }
        bool d = x > t->val;
        erase(t->ch[d], x);
        t->update();
    }
    void replace(int x, int y) {
        erase(x); insert(y);
    }
    int calckth(int k) {
        if (k < 0 || k > root->sz) return 0;
        k = root->sz-k+1;
        bool d;
        for (node* t = root; t != null; t = t->ch[d]) {
            int cnt = t->ch[0]->sz;
            if (k == cnt+1) return t->val;
            else if (k <= cnt) d = 0;
            else d = 1, k -= (cnt+1);
        }
    }
    void mergeto(node*& t, treap* tr) {
        if (t->ch[0] != null) mergeto(t->ch[0], tr);
        if (t->ch[1] != null) mergeto(t->ch[1], tr);
        tr->insert(t->val);
        delete t; t = null;
    }
    void mergeto(treap* T) { mergeto(root, T); }
    void erase(int x) { erase(root, x); }
    void insert(int x) { insert(root, x); }
    int size() { return root->sz; }
}** Treap = new treap *[maxn];
void addEdge(int u, int v) {
    edge[tot] = Edge(v, ne[u]);
    ne[u] = tot++;
}
void dfs(int u, int f) {
    vis[u] = true, fa[u] = f;
    for (int i = ne[u]; ~i; i = edge[i].ne) {
        int v = edge[i].to;
        if (vis[v]) continue;
        dfs(v, f);
    }
}
int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }
void unionn(int x, int y) {
    x = find(x), y = find(y);
    if (x == y) return;
    if (sz[x] > sz[y]) swap(x, y);
    fa[x] = y; sz[y] += sz[x];
    Treap[x]->mergeto(Treap[y]);
}
int kas;
void work() {
    tot = 0;
    memset(ne, -1, sizeof(ne));
    memset(sz, 0, sizeof(sz));
    memset(flag, 0, sizeof(flag));
    memset(vis, 0, sizeof(vis));
    for (int i = 1; i <= n; ++i) scanf("%d", &val[i]);
    for (int i = 1; i <= m; ++i) {
        scanf("%d%d", &u[i], &v[i]);
    }
    int num = 0; char ch;
    while (scanf("\n%c", &ch) && ch != 'E') {
        ++num;
        int x, y, p, w;
        if (ch == 'D') {
            scanf("%d", &x);
            flag[x] = true;
            oper[num] = Oper(0, x);
        }
        else if (ch == 'Q') {
            scanf("%d%d", &x, &y);
            oper[num] = Oper(1, x, y);
        }
        else if (ch == 'C') {
            scanf("%d%d", &p, &w);
            oper[num] = Oper(2, p, val[p]);
            val[p] = w;
        }
    }
    for (int i = 1; i <= m; ++i) {
        if (!flag[i]) {
            addEdge(u[i], v[i]); addEdge(v[i], u[i]);
        }
    }
    for (int i = 1; i <= n; ++i) if (!vis[i]) dfs(i, i);
    for (int i = 1; i <= n; ++i) {
        Treap[i] = new treap;
        ++sz[fa[i]];
        Treap[fa[i]]->insert(val[i]);
    }
    LL ans = 0, cnt = 0;
    for (int i = num; i > 0; --i) {
        if (oper[i].t == 0) unionn(u[oper[i].x], v[oper[i].x]);
        else if (oper[i].t == 1) {
            ans += (LL)Treap[find(oper[i].x)]->calckth(oper[i].y), ++cnt;
        }
        else {
            Treap[find(oper[i].x)]->replace(val[oper[i].x], oper[i].y);
            val[oper[i].x] = oper[i].y;
        }
    }
    printf("Case %d: %.6f\n", ++kas, 1.0 * ans / cnt);
}
int main() {
    while (scanf("%d%d", &n, &m) != EOF && n + m) work();
    return 0;
}

若干bug…。

  1. delete t; 后忘了写 t = null;
  2. erase 掉一个节点后忘了 return;
  3. vis 数组忘了初始化
  4. treap忘了写初始化部分 root = null;
  5. 并查集忘了初始化 sz=1

就是最后一个错误,导致一直 TLE ,教训惨痛。
花了一个小时写,三个半小时对付各种错误…。

另:学习到了动态创建结构体指针数组(?)的方式
treap** Treap = new treap*[maxn];

参考:没有躲过的坑–new一个指针数组、以及创建动态二维数组 ——博览群书1989

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值