Treap LA5031

动态二插排序树的一种常用实现算法:Treap。

从树根向下递归时当然都是一般的BST方式,左小又大,一步步向下;而向上回溯时却是采用维护最大堆的方式,同时为了维护二叉排序树的基本特性,每次不是直接交换节点,而是旋转;

从基本的Treap中为了解题,衍生出了名次树,每个节点用一个数据记录该子树所有节点数,可以完成查询第K大数(第K小数),找任给的x在当前树中的名次;

注意maintain的实现方式和位置,位置很重要,基本原则就是每次更新了节点(添加,删除,旋转),最后都要维护一下;

本题其实还要复杂的多,用到了启发式合并(大概就是从后往前,逆序的意思?),然后同时维护多个Treap树,除了基本的insert,remove,还要完成合并两个树mergoto的操作,自然要用到基本的并查集,哎,白书上说让大家独立手拍;

这题综合性相当强,时间复杂度是n*logn*logn。

#include <cstdlib>

struct node {
    node *ch[2];
    int r;
    int v;
    int s;
    node(int v):v(v) { ch[0]=ch[1]=NULL; r=rand(); s=1; }
    bool operator < (const node& rhs) const {
        return r < rhs.r;
    }
    int cmp(int x) const {
        if (x==v) return -1;
        return x<v? 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* 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);
    else {
        int d=(x < o->v?0 :1 );
        insert(o->ch[d],x);
        if (o->ch[d] > o) rotate(o,d^1);
    }
    o->maintain();
}

void remove(node* &o,int x) {
    int d=o->cmp(x);
    if (d==-1) {
        node* u=o;
        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 {
            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();
}

#include <cstdio>
#include <cstring>
#include <vector>
//#include <fstream>
using namespace std;

const int maxc= 500000+10;
struct Commond {
    char type;
    int x, p;
}commonds[maxc];

const int maxn=20000+10;
const int maxm=60000+10;
int n, m, weight[maxn], from[maxm], to[maxm], removed[maxm];

int pa[maxn];
int findset(int x) {
    return pa[x]!=x ? pa[x]=findset(pa[x]) : x;
}

node* root[maxn];

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 (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 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 removetree(node* &x) {
    if (x->ch[0]!=NULL) removetree(x->ch[0]);
    if (x->ch[1]!=NULL) removetree(x->ch[1]);
    delete x;
    x=NULL;
}

void add_edge(int x) {
    int u=findset(from[x]), v=findset(to[x]);
    if (u!=v) {
        if (root[u]->s<root[v]->s) {
            pa[u]=v; mergeto(root[u],root[v]);
        }
        else {
            pa[v]=u; mergeto(root[v],root[u]);
        }
    }
}

int query_cnt;
long long query_tot;
void query(int x,int k){
    query_cnt++;
    query_tot += kth(root[findset(x)],k);
}

void change_weight(int x,int v) {
    int u=findset(x);
    remove(root[u],weight[x]);
    insert(root[u],v);
    weight[x]=v;
}

int main(){
//    freopen("1.in","r",stdin);

    int kase=0;
    while (scanf("%d%d",&n, &m)==2 && n) {
        for (int i=1;i<=n;i++) scanf("%d",&weight[i]);
        for (int i=1;i<=m;i++) scanf("%d%d",&from[i],&to[i]);
        memset(removed,0,sizeof(removed));

        int c=0;
        for (;;) {
            char type;
            int x, p=0, v=0;
            scanf(" %c",&type);
            if (type=='E') break;
            scanf("%d",&x);
            if (type=='D') removed[x]=1;
            if (type=='Q') scanf("%d",&p);
            if (type=='C') {
                scanf("%d",&v);
                p=weight[x];
                weight[x]= v;
            }
            commonds[c++]= (Commond) {type,x,p};
        }

        for (int i=1;i<=n;i++) {
            pa[i]=i; if (root[i]!=NULL) removetree(root[i]);
            root[i]= new node(weight[i]);
        }
        for (int i=1;i<=m;i++)
            if (!removed[i]) add_edge(i);

        query_tot= query_cnt = 0;
        for (int i=c-1;i>=0;i--) {
            if (commonds[i].type=='D') add_edge(commonds[i].x);
            if (commonds[i].type=='Q') query(commonds[i].x,commonds[i].p);
            if (commonds[i].type=='C') change_weight(commonds[i].x,commonds[i].p);
        }
        printf("Case %d: %.6lf\n",++kase,query_tot/(double)query_cnt);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值