Hdu 3726 Graph and Queries(删边,查询第k大,修改点值)

传送门:[http://acm.split.hdu.edu.cn/showproblem.php?pid=3726](Hdu 3726 Graph and Queries)


题意:给你n个点,m条边,有下面三种操作
1:D X :删掉第X条边。
2:Q X K :查询和X相连的点中第K大的点的值
3:C X V :将点X的权值修改为V


思路:逆向处理,利用并查集+Splay
按值建立Splay,修改点值,先找到这个点,让这个点作为根,删除根
删除边,判断两个点的并查集是否相同,暴力合并(每个点最多被合并logn次),所以合并时间复杂度为logn^2


#include<bits/stdc++.h>
using namespace std;
const int INF=0x3f3f3f3f;
const int N=6e4+10;
const int M=6e4+10;
struct query{
    int op,x,k;
}Q[M*10];
struct Edge{
    int from,to;
}E[M];
vector<int>G[N],Num[N];
int s[N],tot1,tot2,size[N],val[N],fa[N],f[N],root[N],n,Siz[N],flag[N],cnt[N],ch[N][2];
int case1=1;

void Newnode(int &now,int father,int k){
    if(tot2)    now=s[tot2--];
    else    now=++tot1;
    val[now]=k,fa[now]=father,size[now]=1,cnt[now]=1;
    ch[now][0]=ch[now][1]=0;
}

void erase(int x){
    if(!x)  return ;
    s[++tot2]=x;
    erase(ch[x][0]),erase(ch[x][1]);
}

void pushup(int x){
    size[x]=size[ ch[x][0] ]+size[ ch[x][1] ]+cnt[x];
}

//旋转,kind为1为右旋,kind为0为左旋
void Rotate(int x,int kind){
    int y=fa[x];
    ch[y][!kind]=ch[x][kind];
    fa[ch[x][kind]]=y;
    //如果父节点不是根结点,则要和父节点的父节点连接起来
    if(fa[y])
        ch[ fa[y] ][ch[fa[y]][1]==y]=x;
    fa[x]=fa[y];
    ch[x][kind]=y;
    fa[y]=x;
    pushup(y);
}

//Splay调整,将根为now的子树调整为goal
void Splay(int now,int goal,int Belong){
    while(fa[now]!=goal){
        if(fa[ fa[now] ]==goal)
            Rotate(now,ch[ fa[now] ][0]==now);
        else{
            int pre=fa[now],kind=ch[ fa[pre] ][0]==pre; //左儿子为1,右儿子为0
            if(ch[pre][kind]==now){ //两个方向不同
                Rotate(now,!kind);
                Rotate(now,kind);
            }
            else{   //两个方向相同
                Rotate(pre,kind);
                Rotate(now,kind);
            }
        }
    }
    if(goal==0) root[Belong]=now;
    pushup(now);
}

int get_kth(int x,int k){
    int num1=size[ch[x][0]];
    int num2=size[ch[x][0]]+cnt[x];
    if(num1<k&&num2>=k)
        return x;
    if(num1>=k)   return get_kth(ch[x][0],k);
    return get_kth(ch[x][1],k-num2);
}

int find(int x){
    if(x==f[x])
        return f[x];
    f[x]=find(f[x]);
    return f[x];
}
char str[3];

void init(){
    tot1=tot2=0;
    for(int i=1;i<=n;i++){
        Newnode(root[i],0,-INF);
        Newnode(ch[root[i]][1],root[i],INF);
        Newnode(ch[ch[root[i]][1]][0],ch[root[i]][1],Num[i].back());
        pushup(ch[root[i]][1]),pushup(root[i]);
    }
}

void Insert(int k,int Belong){
    int now=root[Belong];
    while(ch[now][val[now]<k]){
        if(val[now]==k){
            Splay(now,0,Belong),cnt[root[Belong]]++;
            pushup(root[Belong]);
            return ;
        }
        now=ch[now][val[now]<k];
    }
    Newnode(ch[now][k>val[now]],now,k);
    //将新插入的结点更新至根结点
    Splay(ch[now][k>val[now]],0,Belong);
    return ;
}

int getmax(int x){
    while(ch[x][1])
        x=ch[x][1];
    return x;
}

void remove(int Belong){
    int m=getmax(ch[root[Belong]][0]);
    Splay(m,root[Belong],Belong);
    ch[m][1]=ch[root[Belong]][1];
    fa[ch[root[Belong]][1]]=m;
    root[Belong]=m;
    fa[root[Belong]]=0;
    pushup(root[Belong]);
}

void Delete(int k,int Belong){
    int now=root[Belong];
    while(ch[now][val[now]<k]){
        if(val[now]==k){
            Splay(now,0,Belong);
            if(cnt[root[Belong]]>1)
                cnt[root[Belong]]--;
            else{
                s[++tot2]=root[Belong];
                remove(Belong);
            }
            pushup(root[Belong]);
            return ;
        }
        now=ch[now][val[now]<k];
    }
    if(val[now]==k){
        Splay(now,0,Belong);
        if(cnt[root[Belong]]>1)
            cnt[root[Belong]]--;
        else{
            s[++tot2]=root[Belong];
            remove(Belong);
        }
        pushup(root[Belong]);
        return ;
    }
}

void Union(int x,int y){
    erase(root[y]),root[y]=0,fa[root[y]]=0;
    for(int i=0;i<G[y].size();i++){
        G[x].push_back(G[y][i]);
        Insert(Num[ G[y][i] ].back(),x);
    }
    G[y].clear();
}

void merge(int from,int to){
    int x=find(from),y=find(to);
    if(x==y)
        return ;
    if(Siz[x]>Siz[y])
        Union(x,y),f[y]=x,Siz[x]+=Siz[y];
    else
        Union(y,x),f[x]=y,Siz[y]+=Siz[x];
}

void travel(int x){
    if(x==0)
        return ;
    travel(ch[x][0]);
    printf("%d ",val[x]);
    travel(ch[x][1]);
}

void change(int x,int k){
    int xx=find(x);
    Num[x].pop_back();
    Delete(k,xx);
    Insert(Num[x].back(),xx);   //更新为这个
}

int main(){
    int m,x;
    while(scanf("%d%d",&n,&m)!=EOF){
        if(n==0&&m==0)
            break;
        memset(flag,0,sizeof(flag));
        for(int i=1;i<=n;i++)   f[i]=i,Siz[i]=1,G[i].clear(),Num[i].clear();
        for(int i=1;i<=n;i++)   scanf("%d",&x),G[i].push_back(i),Num[i].push_back(x);
        for(int i=1;i<=m;i++)
            scanf("%d%d",&E[i].from,&E[i].to);
        int cnt=0;
        for(int i=1;;i++){
            scanf("%s",str);
            if(str[0]=='E'){
                cnt=i-1;
                break;
            }
            if(str[0]=='D')
                Q[i].op=1,scanf("%d",&Q[i].x),flag[Q[i].x]=1;
            else if(str[0]=='Q')
                Q[i].op=2,scanf("%d%d",&Q[i].x,&Q[i].k);
            else
                Q[i].op=3,scanf("%d%d",&Q[i].x,&Q[i].k),Num[Q[i].x].push_back(Q[i].k);
        }
        init();
        for(int i=1;i<=m;i++)
            if(flag[i]==0)
                merge(E[i].from,E[i].to);
        double ans=0;
        int Count=0;
        for(int i=cnt;i>=1;i--){
            if(Q[i].op==1)
                merge(E[Q[i].x].from,E[Q[i].x].to);
            else if(Q[i].op==2){
                Count++;
                int x=find(Q[i].x);
                if(size[root[x]]-Q[i].k>=2&&Q[i].k>=1)
                    ans+=val[get_kth(root[x],size[root[x]]-Q[i].k)];
            }
            else
                change(Q[i].x,Q[i].k);
        }
        printf("Case %d: %.6f\n",case1++,ans/Count);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值