洛谷 P8496 [NOI2022] 众数 题解
作者:互联网
最近 7 年最水的 D1T1。
用权值线段树维护每个数出现的次数,链表维护序列。
操作 4 即合并两棵权值线段树、两个链表,操作 2 就是删除链表尾的元素并在权值线段树上修改。
显然,如果一个序列存在绝对众数,那么它必然等于这个序列的中位数。所以操作 3 就是询问 \(k\) 个序列整体的中位数,并检查这个数的出现次数。
考虑二分中位数,在 \(k\) 棵线段树上分别查询前缀和,再判断出现次数,然而时间复杂度是 \(O(n \log^2 n)\),可能无法通过。把二分中位数改成在 \(k\) 棵线段树上二分即可做到 \(O(n \log n)\)。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6 + 3;
const int SIZE = N * 21;
int n, q, m;
int a[N], siz[N], head[N], tail[N], pre[N];
inline void insert(int i, int p) {
pre[p] = tail[i];
tail[i] = p;
if (!siz[i]) head[i] = p;
++siz[i];
}
inline void erase(int i) {
tail[i] = pre[tail[i]];
--siz[i];
if (!siz[i]) head[i] = 0;
}
inline void link(int i, int j, int k) {
head[k] = siz[i] ? head[i] : head[j];
tail[k] = siz[j] ? tail[j] : tail[i];
siz[k] = siz[i] + siz[j];
if (head[j]) pre[head[j]] = tail[i];
}
int rt[N], ls[SIZE], rs[SIZE], tot;
ll cnt[SIZE];
void update(int &x, int k, int v, int l = 1, int r = n + q) {
if (!x) x = ++tot;
cnt[x] += v;
if (l == r) return;
int mid = (l + r) >> 1;
if (k <= mid) update(ls[x], k, v, l, mid);
else update(rs[x], k, v, mid + 1, r);
}
void merge(int &x, int &y, int l = 1, int r = n + q) {
if (!x || !y) {
x += y;
return;
}
cnt[x] += cnt[y];
if (l == r) return;
int mid = (l + r) >> 1;
merge(ls[x], ls[y], l, mid);
merge(rs[x], rs[y], mid + 1, r);
}
ll query(int x, int k, int l = 1, int r = n + q) {
if (!x) return 0;
if (l == r) return cnt[x];
int mid = (l + r) >> 1;
if (k <= mid) return query(ls[x], k, l, mid);
return query(rs[x], k, mid + 1, r);
}
int c[N], tmp[N], len;
int search(ll k, int l = 1, int r = n + q) {
if (l == r) return l;
int mid = (l + r) >> 1;
ll sum = 0;
for (int i = 1; i <= len; ++i)
sum += cnt[ls[tmp[i]]];
if (sum >= k) {
for (int i = 1; i <= len; ++i)
tmp[i] = ls[tmp[i]];
return search(k, l, mid);
} else {
for (int i = 1; i <= len; ++i)
tmp[i] = rs[tmp[i]];
return search(k - sum, mid + 1, r);
}
}
int main() {
freopen("major.in", "r", stdin);
freopen("major.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n >> q;
for (int i = 1; i <= n; ++i) {
int sz;
cin >> sz;
for (int j = 1; j <= sz; ++j) {
int x;
cin >> x;
a[++m] = x;
insert(i, m);
update(rt[i], x, 1);
}
}
for (int i = 1; i <= q; ++i) {
int op, x, y, z;
cin >> op;
if (op == 1) {
cin >> x >> y;
a[++m] = y;
insert(x, m);
update(rt[x], y, 1);
} else if (op == 2) {
cin >> x;
update(rt[x], a[tail[x]], -1);
erase(x);
} else if (op == 3) {
cin >> len;
ll all = 0, sum = 0;
for (int j = 1; j <= len; ++j) {
cin >> c[j];
tmp[j] = rt[c[j]];
all += siz[c[j]];
}
int mid = search((all + 1) >> 1);
for (int j = 1; j <= len; ++j)
sum += query(rt[c[j]], mid);
if (sum * 2 > all)
cout << mid << '\n';
else
cout << "-1\n";
} else {
cin >> x >> y >> z;
link(x, y, z);
merge(rt[x], rt[y]);
rt[z] = rt[x];
}
}
return 0;
}
标签:rt,head,P8496,int,题解,ll,tail,NOI2022,siz 来源: https://www.cnblogs.com/2ha-maomao-2006/p/p8496-solution.html