题意:
给定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;
}