1006 Finding a MEX(hdu6756)
题意:有一个无向图,n个点m条边(n,m≤1e5),每个点都有一个点权
A
u
A_u
Au(
A
u
A_u
Au≤1e9),有两种操作(操作数≤1e5):
操作1为 1 u x,是直接把u点的点权改成x。
操作2为 2 u,是询问u所有出边连向的点的点权中没出现的最小的非负整数是多少。
解法:首先,可以保证对于每个顶点,操作2的答案最大就是它的度。而最多有1e5条边,那么度数大于等于350的点最多只有350个,运用到这个性质,那么可以把点分为两部分,度数大于350的以及度数小于350的。对于度小于350的那部分,直接暴力求是 O ( n n ) O(n\sqrt n ) O(nn),而对于大于350的那部分,要求最小的未出现的非负整数可以选择二分+树状数组,统计权值出现的次数,二分答案,用树状数组检测答案是否正确,要特判一下0嗷,树状数组上插入0会死循环。
代码:
#include<cstdio>
#include<vector>
#include<cmath>
#include<cstring>
using namespace std;
const int N = 2e5+10;
int n, m, u, v, q, opt, x;
int val[N], a[455], isbig[N];
vector<int> tu[N], tr[N], btu[N], num[N];
//树状数组
int lowbit(int x) { return x & -x; }
void upd(int x, int p, int d) {
for( ; p <= tu[x].size(); p += lowbit(p)) tr[x][p] += d;
}
int que(int x, int p) {
int ans = 0;
for( ; p > 0; p -= lowbit(p)) ans += tr[x][p];
return ans;
}
int main() {
int T; scanf("%d", &T);
while(T--) {
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; ++i) {
scanf("%d", &val[i]);
isbig[i] = 0;
tu[i].clear(); btu[i].clear(); num[i].clear(); tr[i].clear();
}
for(int i = 0; i < m; ++i) {
scanf("%d%d", &u, &v);
tu[u].push_back(v);
tu[v].push_back(u);
}
int sq = 450, len;
for(int i = 1; i <= n; ++i) {
if(tu[i].size() >= sq) {
len = tu[i].size(); isbig[i] = 1;
tr[i].resize(len+2, 0);
num[i].resize(len+2, 0);
for(int j = 0; j <= len; j++) tr[i][j] = num[i][j] = 0;
for(int j = 0, mid; j < len; ++j) {
mid = val[tu[i][j]]; btu[tu[i][j]].push_back(i);
if(mid > len) continue;
++num[i][mid];
if(mid && num[i][mid] == 1) upd(i, mid, 1);
}
}
}
scanf("%d", &q);
while(q--) {
scanf("%d%d", &opt, &u);
if(opt == 1) {
scanf("%d", &x);
if(val[u] == x) continue;
len = btu[u].size();
for(int i = 0, v; i < len; ++i) {
v = btu[u][i];
if(val[u] <= tu[v].size()) {
--num[v][val[u]];
if(val[u] && !num[v][val[u]]) upd(v, val[u], -1);
}
if(x <= tu[v].size()) {
++num[v][x];
if(x && num[v][x] == 1) upd(v, x, 1);
}
}
val[u] = x;
} else {
if(isbig[u]) {
if(!num[u][0]) {
puts("0"); continue;
}
int l = 1, r = tu[u].size(), mid, ans = 0;
while(l <= r) {
mid = l + r >> 1;
if(que(u, mid-1) == mid-1) ans = mid, l = mid + 1;
else r = mid - 1;
}
printf("%d\n", ans);
} else {
len = tu[u].size();
for(int i = 0; i <= len; i++) a[i] = 0;
for(int i = 0, mid; i < len; ++i) {
mid = val[tu[u][i]];
if(mid <= len) ++a[mid];
}
for(int i = 0; i <= len; ++i)
if(!a[i]) { printf("%d\n", i); break; }
}
}
}
}
return 0;
}