[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}}\) 最优
然而,本题涉及卡常,上面的做法对于时空复杂度来说均不能通过。
优化如下:
- 将哈希表改为,对每个查询按时间顺序计算修改的贡献。
- 提前开好数组和计算大小,访问连续内存。(此优化超乎想象的强力!)
- 只对集合内元素个数大于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';
}
}

浙公网安备 33010602011771号