hdu6756 Finding a MEX(根号分治+树状数组求mex)

题意:

给定n个点m条边的无向图,每个点有点权a(i),
现在要进行q次操作,操作有两种:
(1,x,val),将a(x)修改为val
(2,x),查询与点x相邻的点的点权集合的mex

数据范围:n,m,q<=1e5,0<=点权<=1e9

解法:

这种暴力不可做但是又想不到log解法的题,就试着想分块

令sq=sqrt(n),
度数大于sq的点的数量一定不超过sq个,sqrt(1e5)只有300多

查询mex操作:
1.对度数小于sq的点暴力查询,
2.对度数大于sq的超级点每个点开一个树状数组(总共最多开sq个),查询则利用树状数组+二分 查询

修改操作:
一个点的点权修改,那么相邻的超级点的树状数组需要修改,维护一下相邻超级点的树状数组就行了。

code:

#include<bits/stdc++.h>
using namespace std;
const int maxm=1e5+10;
struct BIT{
    int c[maxm];//bit数组
    int cc[maxm];//桶数组
    int v_max;
    int lowbit(int i){
        return i&-i;
    }
    void init(int v_maxx){
        v_max=v_maxx;//数组最大值
        for(int i=1;i<=v_max;i++){//清空bit数组和桶数组
            c[i]=cc[i]=0;
        }
    }
    void add(int i,int t){
        cc[i]+=t;
        if((cc[i]==1&&t==1)||(cc[i]==0&&t==-1)){
            while(i<=v_max)c[i]+=t,i+=lowbit(i);
        }
    }
    int ask(int i){
        int ans=0;
        while(i)ans+=c[i],i-=lowbit(i);
        return ans;
    }
    int Mex(){
        int l,r=v_max;
        int ans=1;
        while(l<=r){
            int mid=(l+r)/2;
            if(ask(mid)==mid)ans=mid,l=mid+1;
            else r=mid-1;
        }
        return ans+1;
    }
}bit[400+5];
vector<int>g[maxm];
vector<int>gg[maxm];
int rt[maxm],tot;
int d[maxm];
int a[maxm];
int n,m;
signed main(){
    int T;scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);
        int sq=sqrt(n)+1;
        //init
        for(int i=1;i<=n;i++){
            g[i].clear();
            gg[i].clear();
            rt[i]=0;
            d[i]=0;
        }
        tot=0;
        //
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
            a[i]++;//因为树状数组不能存储0,所以本题所有权值+1,查询的时候-1就行了
        }
        for(int i=1;i<=m;i++){
            int a,b;scanf("%d%d",&a,&b);
            g[a].push_back(b);
            g[b].push_back(a);
            d[a]++,d[b]++;
        }
        for(int i=1;i<=n;i++){
            if(d[i]>sq){
                rt[i]=++tot;
                bit[rt[i]].init(d[i]);
                for(int v:g[i]){//存储相邻的超级点,数量<sq
                    gg[v].push_back(i);//预处理相邻超级点
                    if(a[v]<=d[i]){//相邻点权值加入bit,太大的不用加入
                        bit[rt[i]].add(a[v],1);
                    }
                }
            }
        }
        int q;scanf("%d",&q);
        while(q--){
            int op;scanf("%d",&op);
            if(op==1){//change
                int x,val;scanf("%d%d",&x,&val);
                val++;
                for(int v:gg[x]){//修改超级点的bit
                    if(a[x]<=d[v]){//删掉之前的
                        bit[rt[v]].add(a[x],-1);
                    }
                    if(val<=d[v]){//加入新的
                        bit[rt[v]].add(val,1);
                    }
                }
                a[x]=val;
            }else if(op==2){//ask
                int x;scanf("%d",&x);
                if(d[x]>sq){//超级点直接在bit查询
                    int ans=bit[rt[x]].Mex();
                    ans--;
                    printf("%d\n",ans);
                }else{//非超级点暴力查询
                    set<int>s;
                    for(int v:g[x]){
                        s.insert(a[v]);
                    }
                    int ans=1;
                    for(auto i:s){
                        if(i==ans)ans++;
                        else break;
                    }
                    ans--;
                    printf("%d\n",ans);
                }
            }
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_44178736/article/details/107520179