1. 程式人生 > 實用技巧 >[圖論分塊][樹狀陣列] HDU 6756 Finding a MEX

[圖論分塊][樹狀陣列] HDU 6756 Finding a MEX

題目大意

給一張 \(n\) 個點 \(m\) 條邊的無向圖 \((1\leq n,m\leq 10^5)\)。結點 \(u\) 的點權為 \(A_u\)

現在要求實現兩種操作:
1 u x : 把結點 \(u\) 的點權修改為 \(x,(0\leq x\leq 10^9)\)
2 u : 詢問結點 \(u\) 鄰接到的所有點的點權的mex。

題解

修改每個點只會影響這個點的鄰域,如果暴力去修改,因為點的度數可能很大,將會TLE。按照圖論分塊的套路,設結點 \(u\) 的度數為 \(deg[u]\),令度數小於等於 \(\sqrt m\) 的點為輕點,度數大於 \(\sqrt m\) 的點為重點。輕點向所有點連邊,重點只向重點連邊,這樣每個點鄰接的點的數量都在 \(O(\sqrt m)\)

級別。

對於每個重點,我們開一個權值樹狀陣列來儲存它鄰接到的所有點的點權,每次去二分樹狀陣列的字首和來求mex。對於修改操作,每次修改同時都要更新相鄰的重點的樹狀陣列。對於詢問操作,如果詢問的是輕點,則 \(O(\sqrt m)\) 暴力遍歷它鄰接的結點求mex,如果詢問的是重點,則直接樹狀陣列求mex。

Code

#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <cstdio>
#include <vector>
#include <cmath>
using namespace std;

#define RG register int
#define LL long long

template<typename elemType>
inline void Read(elemType &T){
    elemType X=0,w=0; char ch=0;
    while(!isdigit(ch)) {w|=ch=='-';ch=getchar();}
    while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    T=(w?-X:X);
}

inline int lowbit(int x){return x&(-x);}

struct BIT{
    int *Node,*cnt;
    int N;
    void set_Len(int len){
        N=len;
        delete Node;delete cnt;
        Node=new int[N+1]();cnt=new int[N+1]();
    }
    void clear(){N=0;delete Node;delete cnt;Node=cnt=nullptr;}
    void Update(int x,int Add){for(;x<=N;x+=lowbit(x)) Node[x]+=Add;}
    int PrefixSum(int x){int Res=0;for(;x;x-=lowbit(x)) Res+=Node[x];return Res;}
    int Query(int L,int R){return PrefixSum(R)-PrefixSum(L-1);}
    void Insert(int x){
        ++x;if(x>N) return;
        ++cnt[x];
        if(cnt[x]==1) Update(x,1);
    }
    void Delete(int x){
        ++x;if(x>N) return;
        if(!cnt[x]) return;
        --cnt[x];
        if(!cnt[x]) Update(x,-1);
    }
    int Mex(){
        int Res=0,L=1,R=N;
        while(L<=R){
            int mid=(L+R)>>1;
            if(PrefixSum(mid)<mid){Res=mid;R=mid-1;}
            else L=mid+1;
        }
        return Res-1;
    }
};
BIT Tree[450];

struct Graph{
    struct edge{int Next,to;};
    edge G[200010];
    int head[100010];
    int cnt;

    Graph():cnt(2){}
    void clear(int node_num=0){
        cnt=2;
        if(node_num==0) memset(head,0,sizeof(head));
        else fill(head,head+node_num+5,0);
    }
    void add_edge(int u,int v){
        G[cnt].to=v;
        G[cnt].Next=head[u];
        head[u]=cnt++;
    }
};
Graph G;
int Value[100010],deg[100010],U[100010],V[100010],ID[100010];
bool heavy[100010];
int N,M,T,Q,block,Index=0;

void Change(int u,int x){
    for(int i=G.head[u];i;i=G.G[i].Next){
        int v=G.G[i].to;
        if(heavy[v]){
            Tree[ID[v]].Delete(Value[u]);
            Tree[ID[v]].Insert(x);
        }
    }
    Value[u]=x;
}

int temp[100010];
int Query(int u){
    if(heavy[u]) return Tree[ID[u]].Mex();
    fill(temp,temp+deg[u]+1,0);
    for(int i=G.head[u];i;i=G.G[i].Next){
        int v=G.G[i].to;
        if(Value[v]>deg[v]+1) continue;
        ++temp[Value[v]];
    }
    for(RG i=0;i<=deg[u]+1;++i)
        if(!temp[i]) return i;
    return 0;
}

int main(){
    Read(T);
    while(T--){
        Read(N);Read(M);
        for(RG i=0;i<=Index;++i)
            Tree[i].clear();
        G.clear(N);block=sqrt(M);Index=0;
        for(RG i=1;i<=N;++i){
            Read(Value[i]);
            heavy[i]=false;
            deg[i]=0;
        }
        for(RG i=1;i<=M;++i){
            Read(U[i]);Read(V[i]);
            ++deg[U[i]];++deg[V[i]];
        }
        for(RG i=1;i<=N;++i){
            if(deg[i]>block){
                heavy[i]=true;ID[i]=++Index;
                Tree[ID[i]].set_Len(deg[i]+1);
            }
        }
        for(RG i=1;i<=M;++i){
            int u=U[i],v=V[i];
            if(!heavy[u] || heavy[v]) G.add_edge(u,v);
            if(!heavy[v] || heavy[u]) G.add_edge(v,u);
            if(heavy[u]) Tree[ID[u]].Insert(Value[v]);
            if(heavy[v]) Tree[ID[v]].Insert(Value[u]);
        }
        Read(Q);
        while(Q--){
            int opt,u,x;
            Read(opt);
            if(opt==1){Read(u);Read(x);Change(u,x);}
            else{Read(u);printf("%d\n",Query(u));}
        }
    }
    return 0;
}