HDU 2020多校 6756 Finding a MEX 简单树分块

我主要是按题解思路写的  

我们发现修改和询问两个操作都和点的度直接相关,直接修改或者询问的话复杂度无法保证(菊花图 度接近n)  那我们得想办法进行一个分类讨论

我们设置大点和小点。设置一个临界值为sqn  其值为根号n

如果一个点的度大于等于根号n 那么我们把它分为大点

否则我们把它分为小点 

首先我们用树状数组维护mex(对每个点开一个树状数组) 并且 将所有的点和与它相连的大点构成另外一个大点图 (在这个图中 对于点u来说 和它相连的都是大点)

对于修改操作   如果当前点是小点 我们直接暴力更新  因为它的度小于sqn  所以单次复杂度是 sqn*logn的   如果是大点我们直接更新  单次复杂度 o(1)

对于询问操作 因为小点的信息已经到位了  假设当前询问的点为 u  我们暴力遍历u的大点图  将大点的信息 放入u的树状数组 

求一次mex  完成后再撤销  因为大点的个数不超过 sqn个  所以复杂度为  sqn*logn

如此下来总的复杂度 是 q*sqn*logn

另外树状数组只要开 d[i]大小  d[i]为i点的度数  我们知道每个点i 的答案不会超过d[i] 所以对于大于d[i]的权值我们也不必要讨论

  

#include<bits/stdc++.h> 
using namespace std;
const int N = 1e5+10;
int n,m,sqn=350,d[N],val[N];
int bh[N],bnex[N<<1],bto[N<<1],bcur;
int h[N],nex[N<<1],to[N<<1],cur;
const int inf = 1e9;
vector<int>BIT[N],cnt[N]; 
inline int in(){
    int x=0;char c=0;
    while(c<'0'||c>'9') c=getchar();
    while(c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return x;
}
void badd_edge(int x,int y){
    bto[++bcur]=y;bnex[bcur]=bh[x];bh[x]=bcur;
}
void add_edge(int x,int y){
    to[++cur]=y;nex[cur]=h[x];h[x]=cur;
}
void init(){
    for(int i = 0; i <= n; i++){
        BIT[i].clear();
        cnt[i].clear();
        h[i]=bh[i]=d[i]=0;
    } 
    bcur=cur=0;
}
void add(int id,int x,int val){
    while(x<=d[id]){
        BIT[id][x]+=val;
        x+=x&-x;
    }
}
int query(int id,int x){
    int ret=0;
    while(x){
        ret+=BIT[id][x];
        x-=x&-x;
    }
    return ret;
}
int getans(int id){
    if(!cnt[id][0]) return 0;
    int l=0,r=d[id],ans;
    while(l<=r){
        int mid = (l+r>>1);
        if(query(id,mid)==mid) l=mid+1,ans=mid;
        else r=mid-1;
    }
    return ans+1; 
}
int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        n=in(),m=in();
        for(int i = 1; i <= n; i++) val[i]=in();
        for(int i = 1; i <= m; i++){
            int u,v;
            u=in();v=in();
            add_edge(u,v);add_edge(v,u);
            d[v]++,d[u]++;
        }
        for(int i = 1; i <= n; i++){
            BIT[i].resize(d[i]+3);
            cnt[i].resize(d[i]+3);
        }
        int q;
        q=in();
        for(int i = 1; i <= n; i++){
            for(int j = h[i]; j; j = nex[j]){
                int v = to[j];
                if(d[v]>=sqn){
                    badd_edge(i,v);
                }
                if(d[i]<sqn){
                    if(val[i]<=d[v]){
                        cnt[v][val[i]]++;
                        if(cnt[v][val[i]]==1&&val[i])
                        add(v,val[i],1);
                    }
                }
            }
        }
        for(int i = 1; i <= q; i++){
            int op,x,y;
            op=in(),x=in();
            if(op==2){
                for(int j = bh[x]; j; j = bnex[j]){
                    int v = bto[j];
                    if(val[v]<=d[x]){
                        cnt[x][val[v]]++;
                        if(cnt[x][val[v]]==1&&val[v])
                        add(x,val[v],1);
                    }
                }
                printf("%d\n",getans(x));
                for(int j = bh[x]; j; j = bnex[j]){
                    int v = bto[j];
                    if(val[v]<=d[x]){
                        cnt[x][val[v]]--;
                        if(cnt[x][val[v]]==0&&val[v])
                        add(x,val[v],-1);
                    }
                }
            }else{
                y=in();
                if(d[x]<sqn){
                    for(int j = h[x]; j; j = nex[j]){
                        int v = to[j];
                        if(val[x]<=d[v]){
                            cnt[v][val[x]]--;
                            if(cnt[v][val[x]]==0&&val[x]) 
                            add(v,val[x],-1);
                        }
                        if(y<=d[v]){
                            cnt[v][y]++;
                            if(cnt[v][y]==1&&y)
                            add(v,y,1);
                        }
                    }
                }
                val[x]=y;
            }
        //    for(int i = 1; i <= n; i++) printf("i=%d rt[i]=%d\n",i,mi[rt[i]]); 
        }
        init();
    }
    return 0;
}
 /*
2
5 4
0 1 2 0 1
1 1
2 1
3 1
5 4
5
2 2
1 2 2
2 2
1 3 1
2 1
*/

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值