[P5607 [Ynoi2013] 无力回天 NOI2017]

P5607 [Ynoi2013] 无力回天 NOI2017
对元素插入次数进行根号分治,插入次数小于 B 的元素用哈希表维护插入集合,每次暴力更新和查询复杂度都为 \(O(B)\) ,次数大于 B 的元素不会超过 \(\frac{m}{B}\) 个, 开 n 个这么大的 bitset 存储,更新 \(O(1)\),查询 \(O(\frac{m^2}{Bw})\), 总复杂度 \(O(m(B+\frac{m}{Bw}))\)\(B=\sqrt{\frac{m}{w}}\) 最优

然而,本题涉及卡常,上面的做法对于时空复杂度来说均不能通过。
优化如下:

  1. 将哈希表改为,对每个查询按时间顺序计算修改的贡献。
  2. 提前开好数组和计算大小,访问连续内存。(此优化超乎想象的强力!)
  3. 只对集合内元素个数大于2的集合开bitset,bitset可以少开一半
#include<bits/stdc++.h>
using namespace std;
#define lowbit(x) (x & (-x))
const int N = 1e6 + 5, B = 130, G = N / B;
int m, cnt[N], id[N], tot, ans[N], val[N], sum[N], buk[N*(B/2+10)];
int *F[N];
vector<int> S[N];
bitset<G> bit[N/2+5];
struct QUE{int op, x, y;} q[N];
struct Node{
    int num = 0, tmp, p = 0, cnt = 0;
    void add(int x) {
        num++;
        if (num == 2) {
            p = ++tot;
            bit[p].set(tmp);
        }
        if (num >= 2) bit[p].set(x);
        else tmp = x;
    }
}a[N];
int calc(Node x, Node y) {
    bitset<G> tmpx, tmpy;
    if (x.num == 1) tmpx.set(x.tmp);
    else tmpx = bit[x.p];
    if (y.num == 1) tmpy.set(y.tmp);
    else tmpy = bit[y.p];
    return (tmpx & tmpy).count();
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr); cout.tie(nullptr);
    cin >> m;
    for (int i = 1; i <= m; i++) {
        cin >> q[i].op >> q[i].x >> q[i].y;
        if (q[i].op == 1) {cnt[q[i].y]++;}
    }
    for (int i = 1; i <= m; i++) if (cnt[i] > B) id[i] = ++tot;
    tot = 0;
    for (int i = 1; i <= m; i++) {
        int x = q[i].x, y = q[i].y;
        if (q[i].op == 1) {
            sum[x]++;
            if (id[y]) a[x].add(id[y]);
            else {
                a[x].cnt += S[y].size();
                S[y].push_back(x);
            }
        }
        else {
            if (x == y) {ans[i] = sum[x]; continue;}
            ans[i] = sum[x] + sum[y] - calc(a[x], a[y]);
            a[x].cnt++; a[y].cnt++;
        }
    }
    F[0] = buk;
    for (int i = 1; i <= m; i++) F[i] = F[i - 1] + a[i - 1].cnt, S[i].clear();
    for (int i = 1; i <= m; i++) {
        int x = q[i].x, y = q[i].y;
        if (q[i].op == 1) {
            if (id[y]) continue;
            else {
                for (int j : S[y]) F[x][0] = j, ++F[x];
                S[y].push_back(x);
            }
        }
        else {
            if (x == y) {continue;}
            F[x][0] = -i; F[y][0] = -i; ++F[x]; ++F[y];
        }
    }
    for (int i = 1; i <= m; i++) F[i] -= a[i].cnt;
    for (int i = 1; i <= m; i++) {
        for (int j = 0; j < a[i].cnt; j++) {
            if (F[i][j] > 0) val[F[i][j]]++;
            else {
                int v = -F[i][j];
                if (q[v].x == i) ans[v] -= val[q[v].y];
                else ans[v] -= val[q[v].x];
            }
        }
        for (int j = 0; j < a[i].cnt; j++) {
            if (F[i][j] > 0) val[F[i][j]]--;
        }
    }
    for (int i = 1; i <= m; i++) {
        if (q[i].op == 2) cout << ans[i] << '\n';
    }
}
posted @ 2025-08-24 15:45  lyrrr  阅读(8)  评论(0)    收藏  举报