题目大意:有一个无向图,n个点,m条边,每个点有一个权值,有四种操作:(1)D X 表示把删除第 X 条边(2)Q X K 表示询问与节点 X 连通的所有点中,包括 X 点,第 K 大的点的权值。(3)C X V 表示将第 X 个点的权值改为 V 。(4)E 表示结束操作。让你输出所有询问操作的值的平均值。
思路:正着不好处理,我们倒着来,先把所有操作后的最终图求出来,然后用名次树,对于每一个连通分量建一棵名次树,对于题目中的操作,我们只需要做 删点,插入点,将两棵名次树合并、查询第k大点 这四个操作就可以了,其中合并两棵树时需要注意,设两棵树的节点数为 n1,n2,假设把n1插入到n2,复杂度为 n1*logn2,要把节点数少的加到多的那里去。启发式合并,总时间复杂度为O(n*logn*logn)。
敲的第一道treap题,调试真心好麻烦。。 代码长长一坨,注意要分清各个部分。。= =
代码如下:
#include<cstdio> #include<cstdlib> struct Node { Node* ch[2]; int v,r;//节点权值、随机优先级 int size;//子树大小,即节点总数 Node(int v) : v(v) {ch[0] = ch[1] = NULL;r = rand();size = 1;} bool operator < (const Node &tmp) const { return r < tmp.r; } int cmp(int x) { if(x == v) return -1; else return x<v ? 0:1; } void maintain() { size = 1; if(ch[0]!=NULL) size += ch[0]->size; if(ch[1]!=NULL) size += ch[1]->size; } }; 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); return ; } int d = (x < o->v )? 0: 1; insert(o->ch[d],x); if(o < o->ch[d]) rotate(o,d^1); o->maintain(); } void remove(Node* &o,int x) { int d = o->cmp(x); if(d == -1) { if(o->ch[0]!=NULL && o->ch[1]!=NULL) { int d2 = o->ch[0] > o->ch[1] ? 1: 0; rotate(o,d2);remove(o->ch[d2],x); } else { Node* u = o; if(o->ch[0] == NULL) o = o->ch[1]; else o = o->ch[0]; delete u; } } else remove(o->ch[d],x); if(o != NULL) o->maintain(); } void debug(Node* o) { if(o == NULL) return ; printf("%d(",o->v); if(o->ch[0]!=NULL) printf("%d,",o->ch[0]->v); else printf("NULL,"); if(o->ch[1]!=NULL) printf("%d",o->ch[1]->v); else printf("NULL"); puts(")"); if(o->ch[0]!=NULL) debug(o->ch[0]); if(o->ch[1]!=NULL) debug(o->ch[1]); } #include<cstring> #include<algorithm> using namespace std; typedef __int64 lld; const int MAXN = 22222; const int MAXM = 66666; struct Command { char op; int x,p; Command(){} Command(char c,int a,int b) : op(c),x(a),p(b) {} } command[555555]; int removed[MAXM]; int from[MAXM],to[MAXM],w[MAXN],fa[MAXN];; //名次树 Node* root[MAXN]; int kth(Node* &o,int k) { if(o == NULL || k<=0 || k > o->size) return 0; int s = o->ch[1] == NULL ? 0: o->ch[1]->size; if(k == s + 1) return o->v; else if(k<=s) return kth(o->ch[1],k); else return kth(o->ch[0],k - s - 1); } void remove_tree(Node* &o) { if(o->ch[0] != NULL) remove_tree(o->ch[0]); if(o->ch[1] != NULL) remove_tree(o->ch[1]); delete o; o = NULL; } 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 init_treap(int n) { for(int i = 1;i<=n;i++) { fa[i] = i; if(root[i] != NULL) remove_tree(root[i]); root[i] = new Node(w[i]); } } // 主程序部分 int find_set(int x) { return x == fa[x] ? x : find_set(fa[x]); } void change_w(int x,int v) { int u = find_set(x); remove(root[u],w[x]); insert(root[u],v); w[x] = v; } lld query(int x,int k) { return (lld)kth(root[find_set(x)],k); } void add_edge(int id) { int a = find_set(from[id]),b = find_set(to[id]); if(a!=b) { if(root[a]->size < root[b]->size){fa[a] = b;mergeto(root[a],root[b]);} //将小的合并到大的,否则会TLE else {fa[b] = a;mergeto(root[b],root[a]);} } } char str[11]; int main() { int cas = 0; int n,m; while(~scanf("%d%d",&n,&m)&&(n+m)) { for(int i = 1;i<=n;i++) scanf("%d",&w[i]); for(int i = 1;i<=m;i++) scanf("%d%d",&from[i],&to[i]); memset(removed,0,sizeof(removed)); int query_cnt = 0; int x,p; int cc = 0; for(;;) { scanf("%s",str); if(str[0] == 'E') break; else if(str[0] == 'D') { scanf("%d",&x); removed[x] = 1; } else if(str[0] == 'Q') { scanf("%d%d",&x,&p); query_cnt ++; } else { int v; scanf("%d%d",&x,&v); p = w[x]; w[x] = v; } command[cc++] = Command(str[0],x,p); } init_treap(n); for(int i = 1;i<=m;i++) if(!removed[i]) add_edge(i); /*for(int i = 1;i<=n;i++) { int u = find_set(i); printf("i = %d,u = %d\n",i,u); debug(root[u]); }*/ lld ans = 0;//会爆int,记得要lld for(int i = cc-1;i>=0;i--) { if(command[i].op == 'D') add_edge(command[i].x); else if(command[i].op == 'Q') ans += query(command[i].x,command[i].p); else change_w(command[i].x,command[i].p); } //printf("%I64d,%d\n",ans,query_cnt); printf("Case %d: %.6f\n",++cas,ans/(double)query_cnt); } return 0; }