hdu 3726 Graph and Queries 10天津赛区 离线算法+treap维护名次树
题意
n(n<=2e4)个顶点m(m<=6e4)条边,每个顶点有个权值val_i, 然后有Q(Q<=5e5)次操作.
操作分为三类:
D x : 删除第x条边
Q x k : 查询与节点x关联的所有顶点中第k大
C x V : 将节点x的权值更改为V
输出查询的均值 /sum { Query_val } / Query_num
解题思路
离线算法
对于删除,可以通过将所有操作读入后,从后往前处理。把删除边转换成插入边。
对于查询第k大顶点,我们可以使用 treap维护的名次树 kth来实现
对于修改操作,我们先将原来的值删除,然后再插入新值。 因为我们使用离线逆向处理,则修改操作也会逆向。
关于名次树,只是对于 treap上增加了一个 size(其子节点的数量和+1)然后来实现求第K大 or 小.
核心代码:
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( s+1 == k ) return o->k; if( s >= k ) return kth( o->ch[1], k ); else return kth( o->ch[0], k-(s+1) ); }
解题代码:
View Code
#include<cstdlib> #include<cstdio> #include<algorithm> #include<cstring> using namespace std; typedef long long LL; const int N = 2e4+10; const int M = 6e4+10; // 名次树 struct Node{ Node *ch[2]; int k, r, s; Node(int x):k(x){ch[0]=ch[1]=NULL;r=rand();s=1;} int cmp(int x){ if( k == x ) return -1; return x < k ? 0 : 1; } void maintain(){ s = 1; if( ch[0] != NULL ) s += ch[0]->s; if( ch[1] != NULL ) s += ch[1]->s; } }; void rotate(Node* &o, int d){ Node *t = o->ch[d^1]; o->ch[d^1] = t->ch[d]; t->ch[d] = o; o->maintain(); t->maintain(); o = t; } void insert(Node* &o, int x){ if( o == NULL ) o = new Node(x); else{ int d = x < o->k ? 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 ){ 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{ Node *t = o; if( o->ch[0] == NULL ) o = o->ch[1]; else o = o->ch[0]; delete t; // take into attentions to delete the space of point } } else remove( o->ch[d], x ); if( o != NULL ) o->maintain(); } 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( s+1 == k ) return o->k; if( s >= k ) return kth( o->ch[1], k ); else return kth( o->ch[0], k-(s+1) ); } struct Command{ char type; int x, y; }commands[500010]; int n, m, cnt; int val[N], st[N], from[M], to[M]; bool removed[M]; LL query_sum; int query_num; Node *root[N]; int find( int x ){ return x==st[x]?x:(st[x]=find(st[x]));} 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->k ); delete src; src = NULL; } void addedge( int x ){ int a = find(from[x]), b = find(to[x]); if( a != b ){ if( root[a]->s < root[b]->s ){ st[a] = b; mergeto( root[a], root[b] ); } else{ st[b] = a; mergeto( root[b], root[a] ); } } } 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 Query( int x, int k ){ query_sum += kth( root[ find(x) ], k ); query_num++; } void Update( int x, int k ){ int a = find(x); remove( root[a], val[x] ); insert( root[a], k ); val[x] = k; } void pre_deal(){ char op[2]; int x, y; // Edge ralationship for(int i = 1; i <= n; i++) scanf("%d", &val[i] ); for(int i = 1; i <= m; i++) scanf("%d%d", &from[i],&to[i] ); // Command request cnt = 0; memset( removed, 0, sizeof(removed) ); while( scanf("%s", op) != EOF ){ if( op[0] == 'E' ) break; scanf("%d", &x); if( op[0] == 'D' ) removed[x] = 1; if( op[0] == 'Q' ) scanf("%d", &y); if( op[0] == 'C' ){ scanf("%d", &y); int t = y; y = val[x]; val[x] = t; } commands[cnt].type = op[0]; commands[cnt].x = x; commands[cnt++].y = y; } // init point for(int i = 1; i <= n; i++){ st[i] = i; if( root[i] != NULL ) remove_tree( root[i] ); root[i] = new Node(val[i]); } // init the edge for(int i = 1; i <= m; i++) if( !removed[i] ) addedge( i ); } void solve( int Case ){ query_sum = 0; query_num = 0; for(int i = cnt-1; i >= 0; i-- ){ if( commands[i].type == 'D' ) addedge( commands[i].x ); if( commands[i].type == 'Q' ) Query( commands[i].x, commands[i].y ); if( commands[i].type == 'C' ) Update( commands[i].x, commands[i].y ); } // printf("sum = %lld\n", query_sum ); // printf("num = %d\n", query_num ); printf("Case %d: %.6lf\n", Case, 1.*query_sum/query_num ); } int main(){ int Case = 1; while( scanf("%d%d",&n,&m), n+m ){ pre_deal(); solve( Case++ ); } return 0; }