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;    

}

 

  

你可能感兴趣的:(Graph)