Finding a MEX
题意:给你一张图,每个点有点权,有q次询问,每次询问有两种操作,第一个操作就是修改一个点的点权,第二个操作就是寻找这个点周围的所有点组成集合的mex(代表0-inf中未出现的数)。
题解:分块+set or 二分+线段树
本题我用set,用set维护大于1000的点。其他的小点就是直接暴力。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;
vector<int> G[maxn],now[maxn];
int a[maxn],sz[maxn],id[maxn];
int vis[105][maxn];
set<int> s[maxn];
bool vv[maxn];
int main()
{
int t; scanf("%d",&t);
while(t--)
{
int n,m; scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) { scanf("%d",&a[i]); sz[i] = 0 ; G[i].clear(); now[i].clear(); }
int lim = 1000;
for(int i=1;i<=m;i++)
{
int u,v; scanf("%d%d",&u,&v);
G[u].push_back(v); G[v].push_back(u);
sz[u]++; sz[v]++;
}
int q; scanf("%d",&q);
int cnt = 0;
for(int i=1;i<=n;i++)
{
if(sz[i]>=lim)
{
id[i] = ++cnt;
s[cnt].clear();
for(int j=0;j<=sz[i];j++)
{
s[cnt].insert(j);
vis[cnt][j] = 0;
}
}
}
for(int i=1;i<=n;i++)
{
for(auto v:G[i])
{
if(sz[v]>=lim)
{
if(a[i]<=sz[v])
{
if(++vis[id[v]][a[i]]==1) s[id[v]].erase(a[i]);
}
now[i].push_back(v);
}
}
}
while(q--)
{
int op; scanf("%d",&op);
if(op==1)
{
int x,y; scanf("%d%d",&x,&y);
for(auto v:now[x])
{
if(y<=sz[v])
{
if(++vis[id[v]][y]==1)
s[id[v]].erase(y);
}
if(a[x]<=sz[v])
{
if(--vis[id[v]][a[x]]==0)
s[id[v]].insert(a[x]);
}
}
a[x] = y;
}
else
{
int x; scanf("%d",&x);
if(sz[x]>=lim) printf("%d\n",*(s[id[x]].begin()));
else
{
for(auto v:G[x])
{
if(a[v]<=sz[x]) vv[a[v]] = 1;
}
int res = 0; while(vv[res]) res++;
printf("%d\n",res);
for(auto v:G[x])
{
if(a[v]<=sz[x]) vv[a[v]] = 0;
}
}
}
}
}
}