2020 Multi-University Training Contest 1
hdu 6756 Finding a MEX 线段树
题意:
给你一张图, 每个点有个权值, 你有q次操作 第一种询问 u节点连的点 的权值第一次没用出现的数最小
第二中操作, 修改 u的权值为x
题解:
这题实在是有点恶心, 朝鲜老哥太会卡了, 我优化了好久才没用t.
先说下思路:
我们将点 分为 大点,与小点,所谓的大点就是 该点连的边的个数 $ge sqrt(n) $ 小点就是 (< sqrt(n))
为啥要这样分?
这样分的好处就是 让修改的操作的复杂度变为 sqrt(n) * log(n),
查询的复杂度最坏也是 sqrt(n)
也就是 大于 sqrt(n)的建一颗线段树, 小于的就直接暴力, 这样相互利用优点让总时间复杂度 为
q * sqrt(n) * log(n)
如果你直接这样写, 不把能优化的都优化, 那么肯定t
优化部分:
- 有重边, 所以建图要去重
- 权值线段不要开 1e9, 因为没用必要, 只要权值开到每个节点连的边个个数就行了, 因为你要求的是mex 如果 这些数 有大于 节点的边数, 那么答案一定再节点边数的范围类, 如果 点的边数这个范围都要值, 那么答案一定是0 。
代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 4e5 + 7;
struct segement {
int sum, l, r;
int count;
}tree[60 * N];
vector<int> G[N];
vector<int> mp[N];
int n, mm, q, top = 1, rt[N], a[N];
#define m (l + r) / 2
void up(int v, int pos, int l, int r, int &node) {
if (pos > r) return;
if (!node) node = top++;
if (l == r) {
tree[node].count += v;
if (tree[node].count > 0) {
tree[node].sum = 1;
} else {
tree[node].sum = 0;
}
return;
}
if (pos <= m) up(v, pos, l, m, tree[node].l);
else up(v, pos, m + 1, r, tree[node].r);
tree[node].sum = tree[tree[node].l].sum + tree[tree[node].r].sum;
}
int query(int l, int r, int node) {
if (l == r) return l;
if ((m - l + 1) > tree[tree[node].l].sum) {
return query(l, m, tree[node].l);
} else {
return query(m + 1, r, tree[node].r);
}
}
int du[N];
vector<int>num[N];
int main() {
int t; scanf("%d", &t);
while (t--) {
scanf("%d %d", &n, &mm);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
top = 1;
for (int i = 1; i <= n; i++) {
G[i].clear();
mp[i].clear();
du[i] = 0;
rt[i] = 0;
num[i].clear();
}
for (int i = 1; i <= mm; i++) {
int u, v;
scanf("%d %d", &u, &v);
G[u].push_back(v);
G[v].push_back(u);
}
int block = (int)sqrt(n);
for (int i = 1; i <= n; i++) {
sort(G[i].begin(), G[i].end());
G[i].erase(unique(G[i].begin(), G[i].end()), G[i].end());
du[i] = G[i].size();
}
for (int i = 1; i <= n; i++) {
for (int to: G[i]) {
if (du[to] >= block) {
mp[i].push_back(to);
}
}
}
for (int i = 1; i <= n; i++) {
if (du[i] >= block) {
num[i].resize(du[i] + 2);
for (int to: G[i]) {
if (a[to] <= du[i]) {
if (++num[i][a[to]] == 1)
up(1, a[to], 0, du[i], rt[i]);
}
}
}
}
int q; scanf("%d", &q);
while (q--) {
int op; scanf("%d", &op);
if (op == 1) {
int u, x; scanf("%d %d", &u, &x);
if (a[u] == x) continue;
for (int to: mp[u]) {
if (a[to] <= du[to]) {
if ((--num[to][a[u]]) == 0)
up(-1, a[u], 0, du[to], rt[to]);
}
if (x <= du[to]) {
if (++num[to][x] == 1)
up(1, x, 0, du[to], rt[to]);
}
}
a[u] = x;
} else {
int u; scanf("%d", &u);
if (du[u] >= block) {
int ans = query(0, du[u], rt[u]);
printf("%d
", ans);
} else {
vector<int> v;
v.resize(400);
for (int to: G[u]) {
if (a[to] >= 400) continue;
v[a[to]]++;
}
int ans = 0;
for (int i = 0; i < 400; i++) {
if (v[i] == 0) {
ans = i;
break;
}
}
printf("%d
", ans);
}
}
}
for (int i = 0; i <= top; i++) {
tree[i].count = tree[i].l = tree[i].r = tree[i].sum = 0;
}
}
}