题目大意:一张带点权的无向图,有两种操作:
1、对每个点修改它的点权
2、查询这个点的所有邻接点的权值集合U 的 mex,mex{U} 即 U中没有的最小权值。
整个题目的算法和复杂度受限于节点的度。
首先答案一定在 [0,d[x]],d[x] 是 x 的度。
对于度数较小的点,查询可以维护一个权值数组,暴力枚举邻接点,之后暴力枚举权值,找到答案,修改直接O(1)修改。
对于度数较大的点,可以维护数据结构来维护 mex,查询直接在维护的数据结构上查询,修改时出了修改自己的权值,还要修改对邻接点中度数较大的点的影响。
考虑以 n \sqrt{n} n 作为区分轻节点和重节点的度数大小边界,下面不妨设 n = 300 \sqrt{n} = 300 n=300。
修改时,除了修改自己的权值,还要维护周围最多 300 300 300 个重节点的数据结构。
查询时,对轻节点,最多需要对300个节点暴力,然后查找答案,这部分的复杂度为 O ( 300 ∗ n ) O(300 * n) O(300∗n),对重节点,需要用数据结构来查询一次答案。
如果使用线段树、平衡树、set等查询和修改操作均为 l o g log log 的数据结构,最终复杂度会达到 n n log n n\sqrt n\log n nnlogn。
观察发现,每次修改,最多需要进行 n \sqrt n n 次数据结构修改,而查询只需要一次。
用权值分块数组,可以做到 O ( 1 ) O(1) O(1) 修改, O ( n ) O(\sqrt n) O(n) 查询,均衡之下,总复杂度为 O ( n n ) O(n \sqrt n) O(nn)
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 5e5 + 10;
vector<int> g[maxn], big[maxn];
int t,n,m,a[maxn],q;
int sqr;
struct block {
int n, siz;
vector<int> b;
vector<int> sum;
void init(int tot) {
b.resize(tot + 1);
siz = 300;
sum.resize(tot / siz + 1);
n = tot;
}
void update(int val,int t) {
if (val > n) return ;
if (t == 1) {
b[val] += t;
if (b[val] == 1)
sum[val / siz]++;
} else {
b[val] += t;
if (b[val] == 0)
sum[val / siz]--;
}
}
int qry() {
for (int j = 0; j <= n / siz; j++) {
if (sum[j] < siz) {
int ed = min(n,(j + 1) * siz - 1);
for (int k = j * siz; k <= ed; k++)
if (!b[k]) return k;
}
}
}
void clear() {
for (int i = 0; i <= n; i++)
b[i] = 0;
for (int i = 0; i <= n / siz; i++)
sum[i] = 0;
}
void free() {
sum.resize(0);
b.resize(0);
n = siz = 0;
}
}B[maxn];
int main() {
scanf("%d",&t);
while (t--) {
scanf("%d%d",&n,&m);
sqr = 300;
for (int i = 1; i <= n; i++)
scanf("%d",&a[i]);
for (int i = 1, x, y; i <= m; i++) {
scanf("%d%d",&x,&y);
g[x].push_back(y);
g[y].push_back(x);
}
for (int i = 1; i <= n; i++) {
B[i].init(g[i].size());
}
for (int i = 1; i <= n; i++) {
for (auto it : g[i]) {
B[i].update(a[it],1);
if (g[it].size() > sqr)
big[i].push_back(it);
}
}
scanf("%d",&q);
while (q--) {
int c, x, y;
scanf("%d%d",&c,&x);
if (c == 1) {
scanf("%d",&y);
for (auto v : big[x])
B[v].update(a[x],-1);
a[x] = y;
for (auto v : big[x])
B[v].update(a[x],1);
} else {
if (g[x].size() > sqr)
printf("%d\n",B[x].qry());
else {
B[x].clear();
for (auto v : g[x])
B[x].update(a[v],1);
printf("%d\n",B[x].qry());
}
}
}
for (int i = 1; i <= n; i++) {
B[i].free();
g[i].clear();
big[i].clear();
}
}
return 0;
}