HDU 6756 Finding a MEX 树状数组+根号分治
Posted kaka0010
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HDU 6756 Finding a MEX 树状数组+根号分治相关的知识,希望对你有一定的参考价值。
原题链接:https://acm.hdu.edu.cn/showproblem.php?pid=6756
题意
我们定义一个点u的集合
S
u
Su
Su是所有与u相连的点,其中每个点都都权值
A
u
Au
Au。
两个操作:
- 将u节点的值改为val
- 求F(u),F(u)是 m e x ( S u ) mex(Su) mex(Su)即Su集合中没出现的权值的最小值
分析
容易发现复杂度其实和每个点的度有关,如果一个点的度非常大为N,那么总复杂度就是 O ( N 2 ) O(N^2) O(N2)。
考虑对度进行分块,对于大于 N \\sqrt{N} N的节点肯定不会超过 N \\sqrt{N} N个,因此我们可以用400个树状数组来维护,查询mex时直接二分答案查询。这样复杂度是 n n l o g n n\\sqrt{n}log\\sqrt{n} nnlogn会比直接值域分块要慢一些,但这题是可以过的。对于度小于 N \\sqrt{N} N的节点,我们可以直接暴力遍历。
注意:更新的时候要同时更新临近节点大块的信息
Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned int ul;
typedef pair<int, int> PII;
const int inf = 0x3f3f3f3f;
const int N = 1e5 + 10;
const int M = 1e6 + 10;
const ll mod = 1e9 + 7;
const double eps = 1e-8;
#define lowbit(i) (i & -i)
#define Debug(x) cout << (x) << endl
#define fi first
#define se second
#define mem memset
#define endl '\\n'
struct BIT {
int c[N], cnt[N], siz;
void init() {
for (int i = 0; i <= siz; i++) cnt[i] = c[i] = 0;
}
void add(int x, int v) {
cnt[x] += v;
if ((cnt[x] == 0 && v == -1) || (cnt[x] == 1 && v == 1)) {
for (int i = x; i <= siz; i += lowbit(i))
c[i] += v;
}
}
int ask(int x) {
int res = 0;
for (int i = x; i > 0; i -= lowbit(i))
res += c[i];
return res;
}
int Mex() {
int l = 1, r = siz;
while (l <= r) {
int mid = (l + r) >> 1;
if (ask(mid) == mid) l = mid + 1;
else r = mid - 1;
}
return l;
}
}bit[405];
int d[N], a[N], id[N], cnt;
vector<int> g[N];
inline void solve() {
int T; cin >> T; while (T--) {
int n, m; cin >> n >> m;
for (int i = 1; i <= n; i++) d[i] = 0, g[i].clear();
for (int i = 1; i <= n; i++) cin >> a[i], a[i]++;
for (int i = 1; i <= m; i++) {
int u, v; cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
d[u]++, d[v]++;
}
cnt = 0;
for (int i = 1; i <= n; i++) {
if (d[i] > 400) {
id[i] = ++cnt;
bit[cnt].siz = d[i];
bit[cnt].init();
for (auto v : g[i]) {
if (a[v] <= d[i]) bit[cnt].add(a[v], 1);
}
}
}
int q; cin >> q;
while (q--) {
int opt, x, val;
cin >> opt;
if (opt == 1) {
cin >> x >> val;
for (auto v : g[x]) {//相邻的块是大块
if (d[v] > 400) {
if (a[x] <= d[v]) bit[id[v]].add(a[x], -1);
if (val + 1 <= d[v]) bit[id[v]].add(val+1, 1);
}
}
a[x] = val + 1;
} else {
cin >> x;
if (d[x] > 400) {
printf("%d\\n", bit[id[x]].Mex() - 1);
} else {
vector<int> vis(405);
for (auto v : g[x]) {
if (a[v] <= d[x]) vis[a[v]]++;
}
for (int i = 1; i <= 405; i++) {
if (!vis[i]) {
printf("%d\\n", i - 1);
break;
}
}
}
}
}
}
}
signed main() {
ios_base::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
#ifdef ACM_LOCAL
freopen("input", "r", stdin);
freopen("output", "w", stdout);
signed test_index_for_debug = 1;
char acm_local_for_debug = 0;
do {
if (acm_local_for_debug == '$') exit(0);
if (test_index_for_debug > 20)
throw runtime_error("Check the stdin!!!");
auto start_clock_for_debug = clock();
solve();
auto end_clock_for_debug = clock();
cout << "Test " << test_index_for_debug << " successful" << endl;
cerr << "Test " << test_index_for_debug++ << " Run Time: "
<< double(end_clock_for_debug - start_clock_for_debug) / CLOCKS_PER_SEC << "s" << endl;
cout << "--------------------------------------------------" << endl;
} while (cin >> acm_local_for_debug && cin.putback(acm_local_for_debug));
#else
solve();
#endif
return 0;
}
以上是关于HDU 6756 Finding a MEX 树状数组+根号分治的主要内容,如果未能解决你的问题,请参考以下文章