UVALive 5031 Graph and Queries

来源:互联网 发布:git linux设置ignore 编辑:程序博客网 时间:2024/05/20 06:50

UVALive 5031 Graph and Queries

名次树treap,静态区间第K大,离线算法

刘汝佳白皮234页,讲的很详细,写这博客为了纪念。。这超长的代码。。

传送门:HustOJ

传送门:LA


题意

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


思路

设计一个逆向的离线算法。先读入全部操作,生成最终的图,为每个连通块维护一个名次树。
删边对应加边,若边的两端点位于同一连通块则无所谓,否则合并两个Treap。
修改权值,在操作序列数组里记录修改前的权值,然后修改权值数组中的权值。处理所有操作时在改回来。
查询操作,查询对应Treap中第k大。

关于treap维护第k大:treap本身就是一个二叉搜索树,所以为每个点增加一个值s,表示孩子个数。所以求第k大(当前节点是now):如果k=now右儿子->s+1,说明now就是第k大。如果k小于now右儿子->s+1,说明右面的孩子很多,那么在右孩子里面求第k大。如果大于,说明右面孩子太少了,k大落在左侧。在左儿子中求k-s-1大。


代码

#include <cstdio>#include <cstdlib>#include <iostream>#include <algorithm>#include <string>#include <cstring>#include <vector>#include <cmath>#include <queue>#include <stack>#include <set>#include <map>#define _ ios_base::sync_with_stdio(0),cin.tie(0)#define M(a,b) memset(a,b,sizeof(a))using namespace std;const int MAXN=20007;const int oo=0x3f3f3f3f;typedef long long LL;const LL loo=4223372036854775807ll;typedef long double LB;const LL mod=1e9+7;//名次树struct Node{    Node *ch[2];//左右儿子    int rank;//随机优先级    int val;//值    int s;//孩子总数    Node(int v) { val=v;rank=rand();s=1;ch[0]=ch[1]=NULL; }    int cmp(int x) const    {        if(x==val) return -1;        else return (x<val) ? 0 : 1;    }    void maintain()    {        s=1;        if(ch[0]!=NULL) s+=ch[0]->s;        if(ch[1]!=NULL) s+=ch[1]->s;    }};Node* treaproot[MAXN];void rotate(Node* &o, int d)//d=0代表左旋,d=1代表右旋{    Node* k=o->ch[d^1];o->ch[d^1]=k->ch[d];k->ch[d]=o;    o->maintain();k->maintain();    o=k;}int find(Node* o, int x)//在Treapo中查找x{    while(o!=NULL)    {        int d=o->cmp(x);        if(d==-1) return 1;        else o=o->ch[d];    }    return 0;}void insert(Node* &o, int x)//在Treapo中插入key=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]->rank>o->rank) rotate(o, d^1);    }    o->maintain();}void remove(Node* &o, int x)//在Treapo中移除key为x的元素{    int d=o->cmp(x);    if(d==-1)    {        Node* u=o;        if(o->ch[1]==NULL)        {            o=o->ch[0];            delete u;        }        else if(o->ch[0]==NULL)        {            o=o->ch[1];            delete u;        }        else        {            int d2=(o->ch[0]->rank>o->ch[1]->rank ? 1 : 0);            rotate(o, d2);remove(o->ch[d2], x);        }    }    else    {        remove(o->ch[d], x);    }    if(o!=NULL) o->maintain();}void release(Node* &o)//释放整颗Treap{    if(o->ch[0]!=NULL) release(o->ch[0]);    if(o->ch[1]!=NULL) release(o->ch[1]);    delete o;    o=NULL;}void merge(Node* &a, Node* &b){    if(b->ch[0]!=NULL) merge(a, b->ch[0]);    if(b->ch[1]!=NULL) merge(a, b->ch[1]);    insert(a, b->val);    delete b;b=NULL;}int find_Kth(Node* o, int k){    if(o==NULL||k<=0||k>o->s) return 0;    else    {        Node* now=o;        while(1)        {            int rson=(now->ch[1]==NULL ? 0 : now->ch[1]->s);            if(k==rson+1) { return now->val; }            else if(k<=rson)            {                now=now->ch[1];            }            else            {                now=now->ch[0];                k=k-rson-1;            }        }    }}//名次树//并查集int fa[MAXN];int get_fa(int a){    return a==fa[a] ? a : fa[a]=get_fa(fa[a]);}//并查集int op;int operate[500010][3];//操作指令序列int querycnt=0;LL querysum=0;vector<int> G[MAXN];int val[MAXN];struct Edge{    int from, to, removed;    Edge() {}    Edge(int _a, int _b, int _c) { from=_a;to=_b;removed=_c; }};vector<Edge> edge;void init()//初始化整个{    for(int i=0;i<MAXN;i++) G[i].clear();    edge.clear();    querycnt=querysum=op=0;}void add_edge(int i){    int from=edge[i].from, to=edge[i].to;    int fathera=get_fa(from);    int fatherb=get_fa(to);    if(fathera==fatherb) return;    if(treaproot[fathera]->s>treaproot[fatherb]->s)    {        fa[fatherb]=fathera;        merge(treaproot[fathera], treaproot[fatherb]);    }    else    {        fa[fathera]=fatherb;        merge(treaproot[fatherb], treaproot[fathera]);    }}void query(int x, int k){    int fatherx=get_fa(x);    querycnt++;    querysum+=find_Kth(treaproot[fatherx], k);}void change_weight(int a, int b){    int fa=get_fa(a);    remove(treaproot[fa], val[a]);    insert(treaproot[fa], b);    val[a]=b;}int main(){    _;    int n;    int kase=0;    while(scanf("%d", &n))    {        init();        int m;scanf("%d", &m);        if(n==0) break;        for(int i=1;i<=n;i++) scanf("%d", &(val[i]));        for(int i=0;i<m;i++)        {            int ta, tb;scanf("%d%d", &ta, &tb);            edge.push_back(Edge(ta, tb, 0));        }        while(1)        {            char type;scanf(" %c", &type);            if(type=='E') break;            if(type=='D')            {                int typed;scanf("%d", &typed);                edge[typed-1].removed=1;                operate[op][0]='D'-'A';                operate[op++][1]=typed;            }            if(type=='Q')            {                int ta, tb;scanf("%d%d", &ta, &tb);                operate[op][0]='Q'-'A';                operate[op][1]=ta;operate[op++][2]=tb;            }            if(type=='C')            {                int ta, tb;scanf("%d%d", &ta, &tb);                operate[op][0]='C'-'A';                operate[op][2]=val[ta];                val[ta]=tb;                operate[op++][1]=ta;            }        }        for(int i=1;i<=n;i++)        {            fa[i]=i;            if(treaproot[i]!=NULL) release(treaproot[i]);            insert(treaproot[i], val[i]);        }        for(int i=0;i<m;i++)        {            if(!edge[i].removed)                add_edge(i);        }        for(int i=op-1;i>=0;i--)        {            if(operate[i][0]+'A'=='Q')            {                query(operate[i][1], operate[i][2]);            }            if(operate[i][0]+'A'=='C')            {                change_weight(operate[i][1], operate[i][2]);            }            if(operate[i][0]+'A'=='D')            {                add_edge(operate[i][1]-1);            }        }        printf("Case %d: %.6lf\n", ++kase, querysum/(double)querycnt);    }    return 0;}
0 0
原创粉丝点击