其他分享
首页 > 其他分享> > 洛谷 P8496 [NOI2022] 众数 题解

洛谷 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