UVALive 5031 Graph and Queries

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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值