UVa1479 - Graph and Queries图询问(Treap名次树)

为什么大家写200行的题我要写400行才过啊QAQ。。。。。
详解在代码注释中 这题真的写了好久 从自己写完到、调试到和刘汝佳的标程核对 前后花了半上午+一下午时间QAQ
程序见下 很长确实,但是可读性我觉得比别人是高了很多的

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <queue>
#include <algorithm>
//AC
using namespace std;
#define mem(x) memset(x,0,sizeof(x))
const int maxn=2*100000+50;
const int maxm=6*100000+50;
const int maxc=5*1000000+50;
int n,m;
//0表示左旋 1表示右旋
struct comment
{
    char type;
    int x,p;
    comment(char type=0,int x=0,int p=0)
    {
        this->type=type;
        this->x=x;
        this->p=p;
    }
    void ini()
    {
        type=0;
        x=0;
        p=0;
    }
}c[maxc];

struct node
{
    int r;
    int siz;
    int v;
    node* ch[2];//0表示左儿子 1表示右儿子
    node(int v)
    {
        this->v=v;
        this->siz=1;/
        this->ch[0]=this->ch[1]=NULL;
        this->r=rand();
    }
    int cmp(int x)const
    {
        if(x==v) return -1;
        else return x>v?1:0;
    }
};
node* root[maxn];

int f[maxn];//并查集相关

int getf(int x)
{
    return f[x]==x?f[x]:(f[x]=getf(f[x]));//注意这里要更新f[x]本身
}

void maintain(node* &v)
{
    int s=1;
    if(v->ch[0]!=NULL)
    {
        s+=v->ch[0]->siz;
    }
    if(v->ch[1]!=NULL)
    {
        s+=v->ch[1]->siz;
    }
    v->siz=s;
}

void rotate(node* &o,int d)//旋转操作
{
    node *k=o->ch[d^1];
    o->ch[d^1]=k->ch[d];
    k->ch[d]=o;
    maintain(o);
    maintain(k);
    o=k;
}

void insert(node* &o,int x)
{
    if(o==NULL)
    {
        o=new node(x);
    }
    else
    {
        int d=(o->v)>x?0:1;//一定要注意用cmp比较的话相等的两点会被忽略!
        //这与之前写的treap不同,因为之前的treap不用管相等的元素,这个错误调了很长时间,望注意
        insert(o->ch[d],x);
        if((o->ch[d]->r)>o->r)
        {
            rotate(o,d^1);
        }
        if(o!=NULL)
        {
            maintain(o);
        }
    }
}

void remove(node* &o,int x)
{
    int d=o->cmp(x);
    if(d==-1)//就是节点本身的情况
    {
        if(o->ch[1]!=NULL && o->ch[0]!=NULL)
        {
            int d2=((o->ch[0]->r)>(o->ch[1]->r)?1:0);
            rotate(o,d2);
            remove(o->ch[d2],x);
        }
        else if(o->ch[1]==NULL)
        {
            o=o->ch[0];
        }
        else
        {
            o=o->ch[1];
        }
        if(o!=NULL) maintain(o);
    }
    else
    {
        remove(o->ch[d],x);
        if(o!=NULL) maintain(o);
    }
}

int kth(node* &o,int k)//返回该树根下的第k大的数
{
    if(o==NULL || k<=0 || o->siz<k)/三个条件?->是的,题目说有不合法询问
    {
        return 0;
    }
    int s=(o->ch[1]==NULL?0:o->ch[1]->siz);
    if(s+1==k)
    {
        return o->v;
    }
    else if(s>=k)
    {
        return kth(o->ch[1],k);//因为没有算入本身的节点,所以不用改变k的值
    }
    else
    {
        return kth(o->ch[0],k-s-1);
    }
}

int from[maxm],to[maxm];///树相关、询问相关
int weight[maxn];
bool removed[maxn];//记录删除节点

void getmergeto(node* &par,node* &all)//将par树合并到all
{
    if(par->ch[0]!=NULL)
    {
        getmergeto(par->ch[0],all);
    }
    if(par->ch[1]!=NULL)
    {
        getmergeto(par->ch[1],all);
    }
    if(par!=NULL) maintain(par);
    insert(all,par->v);
    delete par;
    par=NULL;//不加这一句会怎样啊//delete以后存的是垃圾值,不归零就可能出错(因为以后可能访问)
    if(all!=NULL) maintain(all);
}

void removetree(node* &o)//把o及以下所有节点全部删除
{
    if(o->ch[0]!=NULL)
    {
        removetree(o->ch[0]);
    }
    if(o->ch[1]!=NULL)
    {
        removetree(o->ch[1]);
    }
    if(o!=NULL) maintain(o);
    delete o;
    o=NULL;//一旦调用delete 一定要把原指针清零
}

void changeweight(int x,int v)//改变x号的权值至v
{
    int u=getf(x);
    remove(root[u],weight[x]);
    insert(root[u],v);
    weight[x]=v;
}

int query(int x,int k)//与x连通的点中第2大的权值
{
    node* o=root[getf(x)];
    return kth(o,k);
}

void addedge(int x)
{
    int u=from[x];
    int v=to[x];
    u=getf(u);
    v=getf(v);
    if(u!=v)//注意:必须要在u!=v的情况下进行
    {
        if((root[u]->siz)>(root[v]->siz))
        {
            f[v]=u;//
            getmergeto(root[v],root[u]);
            /*
            if(root[u]!=NULL) 
            {
                maintain(root[u]);
            }
            */
        }
        else
        {
            f[u]=v;//
            getmergeto(root[u],root[v]);
            /*
            if(root[v]!=NULL)
            {
                maintain(root[v]);
            }
            */
        }
    }
}

void ini()
{
    for(int i=0;i<maxn;i++)
    {
        f[i]=i;
    }
    mem(from);
    mem(to);
    mem(weight);
    mem(removed);
    for(int i=0;i<maxn;i++)
    {
        root[i]=NULL;
    }
    for(int i=0;i<maxm;i++)
    {
        c[i].ini();
    }
}

int main()
{
    int kas=0;
    while(scanf("%d%d",&n,&m)==2)
    {
        kas++;
        if(n==0)
        {
            break;
        }
        ini();
        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]);
        }

        int qnum=0;//记录所有指令数
        int qsum=0;//记录q指令个数
        while(1)
        {
            char type;
            int x=0,p=0;
            scanf(" %c",&type);
            if(type=='E')
            {
                break;
            }
            else if(type=='D')
            {
                scanf("%d",&x);//删除id为x的边
                removed[x]=true;
            }
            else if(type=='Q')
            {
                scanf("%d%d",&x,&p);
                qsum++;
            }
            else if(type=='C')
            {
                scanf("%d%d",&x,&p);
                int v=weight[x];
                weight[x]=p;
                p=v;
            }
            c[qnum++]=comment(type,x,p);
        }

        for(int i=1;i<=n;i++)
        {
            f[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])
            {
                addedge(i);
            }
        }

        double ans=0;
        for(int i=qnum-1;i>=0;i--)
        {
            char ty=c[i].type;
            if(ty=='Q')
            {
                ans+=query(c[i].x,c[i].p);
            }
            else if(ty=='C')
            {
                changeweight(c[i].x,c[i].p);
            }
            else if(ty=='D')
            {
                removed[c[i].x]=false;
                addedge(c[i].x);
            }
        }
        printf("Case %d: %.6lf\n",kas,(double)ans/(double)qsum);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值