HDU 3726 Graph and Queries Treap
这是一个比较全面的题,涉及到了添加删除寻找第k大还有树的合并。
做法大概先执行所有的删边操作,建立最终的图,这里可以用并查集维护一下, 方便判断是不是在一个联通块中,然后对每个子块建立一个Treap,如果遇到添加边导致两个联通块合并成一个的情况,就将两棵树当中小的那个合并到大的那个里面。因为每次这样的合并操作,必然会有小的那个大小翻倍,其实复杂度是科学的,所以合并操作只要很裸很裸的一个一个节点插入就好。
这题有点考验代码能力,写起来比较的麻烦。
#include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <string> #include <vector> #include <map> #include <set> #include <list> #include <queue> #include <stack> using namespace std; typedef long long LL; const int maxn = 2e4 + 10; const int maxm = 6e4 + 10; const int maxk = 6e5 + 10; //treap struct Node { Node *ch[2]; int rkey, val, size; Node(int v = 0): val(v), size(1) { ch[0] = ch[1] = NULL; rkey = rand(); } int cmp(int x) { if(val == x) return -1; if(val < x) return 1; else return 0; } void maintain() { size = 1; if(ch[0] != NULL) { size += ch[0]->size; } if(ch[1] != NULL) { size += ch[1]->size; } } }; //0表示左旋,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->val ? 0 : 1); insert(o->ch[d], x); if(o->ch[d]->rkey > o->rkey) { rotate(o, d ^ 1); } } o->maintain(); } void remove(Node *&o, int x) { int d = o->cmp(x); Node *u = o; if(d == -1) { if(o->ch[0] == NULL) { o = o->ch[1]; delete u; } else if(o->ch[1] == NULL) { o = o->ch[0]; delete u; } else { int d2 = (o->ch[0]->rkey > o->ch[1]->rkey); rotate(o, d2); remove(o->ch[d2], x); } } else { remove(o->ch[d], x); } if(o != NULL) { o->maintain(); } } void mergetree(Node *&src, Node *&dest) { if(src->ch[0] != NULL) { mergetree(src->ch[0], dest); } if(src->ch[1] != NULL) { mergetree(src->ch[1], dest); } insert(dest, src->val); delete(src); src = NULL; } void remove_tree(Node *&x) { if(x->ch[0] != NULL) { remove_tree(x->ch[0]); } if(x->ch[1] != NULL) { remove_tree(x->ch[1]); } delete(x); x = NULL; } int findkth(Node *root, int k) { if(root == NULL || k > root->size || k <= 0) { return 0; } int rsize = (root->ch[1] == NULL ? 0 : root->ch[1]->size); if(k == rsize + 1) return root->val; if(k <= rsize) return findkth(root->ch[1], k); return findkth(root->ch[0], k - 1 - rsize); } struct Edge { int u, v; Edge(int u = 0, int v = 0): u(u), v(v) {} }; struct Operation { char cmd; int x, k; Operation(char cmd = 0, int x = 0, int k = 0): cmd(cmd), x(x), k(k) {} }; Edge edge[maxm]; Operation opr[maxk]; int n, m, degree[maxn], opcnt; bool isdel[maxm]; int fa[maxn]; Node *root[maxn]; int findset(int x) { return x == fa[x] ? x : fa[x] = findset(fa[x]); } void init() { opcnt = 0; memset(isdel, 0, sizeof(isdel)); for(int i = 1; i <= n; i++) { fa[i] = i; if(root[i] != NULL) { remove_tree(root[i]); } } } void add_edge(int eid) { int u = edge[eid].u, v = edge[eid].v; int x = findset(u), y = findset(v); if(x == y) return; //将节点数小的树合并到节点大的树上 if(root[x]->size < root[y]->size) { fa[x] = y; mergetree(root[x], root[y]); } else { fa[y] = x; mergetree(root[y], root[x]); } } void change_degree(int x, int k) { int y = findset(x); remove(root[y], degree[x]); insert(root[y], k); degree[x] = k; } int query(int x, int k) { return findkth(root[findset(x)], k); } void input() { for(int i = 1; i <= n; i++) { scanf("%d", °ree[i]); } for(int i = 1; i <= m; i++) { int u, v; scanf("%d%d", &u, &v); edge[i] = Edge(u, v); } char cmd; int x, k; while(scanf(" %c", &cmd), cmd != 'E') { if(cmd == 'D') { scanf("%d", &x); isdel[x] = true; } else if(cmd == 'C') { scanf("%d%d", &x, &k); int tmp = degree[x]; degree[x] = k; k = tmp; } else { scanf("%d%d", &x, &k); } opcnt++; opr[opcnt] = Operation(cmd, x, k); } } void build_graph() { for(int i = 1; i <= n; i++) { root[i] = new Node(degree[i]); } for(int i = 1; i <= m; i++) if(!isdel[i]) { add_edge(i); } } int main() { int kase = 1; while(scanf("%d%d", &n, &m), n != 0 && m != 0) { init(); input(); build_graph(); double ans = 0; int qcnt = 0; for(int i = opcnt; i > 0; i--) { if(opr[i].cmd == 'D') { add_edge(opr[i].x); } else if(opr[i].cmd == 'C') { change_degree(opr[i].x, opr[i].k); } else { ans += query(opr[i].x, opr[i].k); qcnt++; } } printf("Case %d: %.6f\n", kase++, ans / qcnt); } return 0; }