[圖論分塊][樹狀陣列] HDU 6756 Finding a MEX
阿新 • • 發佈:2020-09-14
題目大意
給一張 \(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; }