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
两个操作:

  1. 将u节点的值改为val
  2. 求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} nn logn 会比直接值域分块要慢一些,但这题是可以过的。对于度小于 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 树状数组+根号分治的主要内容,如果未能解决你的问题,请参考以下文章

HDU 4747 Mex

关于区间 $mex$ 的几种做法

hdu 4747 线段树/DP

HDU4747:Mex(线段树区间修改)

hdu3294Finding Palindromes

HDU 5992 Finding Hotels(KD树)题解