2020杭电多校第一场 Finding a MEX
题意:一个图,每个点有一个权值,有两种操作,修改一个点的权值和查询一个点周围出现的所有的数中最小的没有出现数。
思路:我们根据点的度数来把度数大于等于 \(\sqrt{n}\) 的点称为大点, 其余为小点,易知大点的个数不超过 \(\sqrt{n}\) , 又易知一个点的 \(F(u) \leq deg(u)\) ,所以, 对于每一个大点,我们单独建立一个树状数组来维护它周围出现的值,当修改一个点的值时,我们直接维护这些大点的树状数组,所以每次修改的复杂度最多为 \(\\logn \sqrt{n}\) ,
对于每次询问,若询问的是小点,就暴力查询周围的所有点, 复杂度为 \(\sqrt{n}\) ,否则通过该节点对应的树状数组进行二分查询,复杂度为 \(logn\) 。所以复杂度为 \(o(ac)\)
#include <bits/stdc++.h>
using namespace std;
#define lc (rt << 1)
#define rc ((rt << 1) | 1)
#define fi first
#define se second
#define pb push_back
#define pii pair<int, int>
#define rep(i, l, r) for (int i = (l); i <= (r); ++i)
#define per(i, r, l) for (int i = (r); i >= (l); --i)
#define PE(i, u) for (int i = head[u]; i != -1; i = edge[i].next)
typedef long long LL;
const int maxn = 1e6 + 20;
const int mod = 1e9 + 7;
struct Edge
{
int to, next;
} edge[maxn];
int k, head[maxn];
int a[maxn], deg[maxn];
void add(int a, int b){
edge[k].to = b;
edge[k].next = head[a];
head[a] = k++;
}
vector<int> vec[550], num[550], g[maxn];
int lowbit(int x) {
return x & (-x);
}
void Update(int x, int i, int k, int cnt){
while(i <= cnt){
vec[x][i] += k;
i += lowbit(i);
}
}
int getsum(int x, int i){
int res = 0;
while(i > 0){
res += vec[x][i];
i -= lowbit(i);
}
return res;
}
int id[maxn];
int vis[maxn];
int main(int argc, char const *argv[])
{
int t;
scanf("%d", &t);
while(t--){
k = 0;
int n, m;
scanf("%d%d", &n, &m);
rep(i, 1, n) {
g[i].clear();
deg[i] = 0;
head[i] = -1;
scanf("%d", &a[i]);
}
rep(i, 1, m){
int u, v;
scanf("%d%d", &u, &v);
add(u, v), add(v, u);
deg[u]++, deg[v]++;
}
int lim = sqrt(n);
int cnt = 0;
rep(u, 1, n){
if(deg[u] >= lim){
id[u] = ++cnt;
vec[cnt].resize(deg[u] + 10), num[cnt].resize(deg[u] + 10);
rep(i, 0, deg[u] + 1) vec[cnt][i] = num[cnt][i] = 0;
PE(i, u){
int to = edge[i].to;
if(a[to] <= deg[u]) {
num[cnt][a[to]]++;
if(num[cnt][a[to]] == 1 && a[to]) {
Update(cnt, a[to], 1, deg[u]);
}
}
}
}
}
rep(u, 1, n){
PE(i, u){
int to = edge[i].to;
if(deg[to] >= lim){
g[u].push_back(to);
}
}
}
int q;
scanf("%d", &q);
while(q--){
int op;
scanf("%d", &op);
if(op == 1){
int u, x;
scanf("%d%d", &u, &x);
int len = g[u].size();
rep(i, 0, len - 1){
int to = g[u][i];
if(a[u] <= deg[to]){
num[id[to]][a[u]]--;
if(num[id[to]][a[u]] == 0 && a[u]){
Update(id[to], a[u], -1, deg[to]);
}
}
if(x <= deg[to]){
num[id[to]][x]++;
if(num[id[to]][x] == 1 && x){
Update(id[to], x, 1, deg[to]);
}
}
}
a[u] = x;
} else {
int u;
scanf("%d", &u);
if(deg[u] < lim){
PE(i, u){
int to = edge[i].to;
if(a[to] <= deg[u]) vis[a[to]] = 1;
}
rep(i, 0, deg[u]){
if(!vis[i]) {
printf("%d\n", i);
break;
}
}
PE(i, u){
int to = edge[i].to;
if(a[to] <= deg[u]) vis[a[to]] = 0;
}
} else {
if(!num[id[u]][0]){
printf("0\n");
} else {
int le = 1, ri = deg[u];
int ans = deg[u];
while(le <= ri){
int mid = (le + ri) >> 1;
int res = getsum(id[u], mid);
if(res == mid){
le = mid + 1;
} else {
ans = mid;
ri = mid - 1;
}
}
printf("%d\n", ans);
}
}
}
}
}
return 0;
}