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

洛谷 P8496 [NOI2022] 众数 题解

时间:2022-08-27 15:34:26浏览次数:61  
标签:rt head P8496 int 题解 ll tail NOI2022 siz

最近 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
From: https://www.cnblogs.com/2ha-maomao-2006/p/p8496-solution.html

相关文章

  • 一切都结束了 - NOI2022 退役记
    大概这篇文章通篇都会用全名。如果有意见可以私信联系我删除部分内容。高一的时候,很喜欢写博客。现在不那么喜欢写博客了,或许是身边有一些人可以分享了。最后一篇博客了......
  • CF1506G 题解
    前言题目传送门!更好的阅读体验?校内考试题目。写一篇题解。思路首先记录每个字符出现了多少次,然后创建单调栈。看当前字符是否入栈,如果没有入栈,就不停pop(),直到:栈......
  • P8444 题解
    前言题目传送门!更好的阅读体验?普及组月赛第二题。特殊数据好恶心啊,考试差点丢分了。思路贪心题,先给\(a\)数组排个序。首先,肯定是买小于等于\(w\)的最大价格的物......
  • CF1550C 题解
    前言题目传送门!更好的阅读体验?比赛时,这题写了一个\(O(n^3)\)算法,然后就过了。以为是数据水,实际上可以证明时间复杂度是\(O(n)\)的。思路关键是一个结论:当\(i<......
  • CF1720C 题解
    前言题目传送门!更好的阅读体验?赛时锁题后看别人代码,怎么都和我想法不一样?幸好没有被hack。思路以下把L字形的覆盖网格,直接称为L。贪心思考,我们想让每次L覆盖......
  • CF1720D1 题解
    前言题目传送门!更好的阅读体验?有点思维难度的DP优化题。小知识在做这道题之前,你需要知道:\(x-y,y-x\lex\oplusy\lex+y\)。证明非常简单,利用异或的性......
  • CF1548B 题解
    前言题目传送门!更好的阅读体验?做法:ST表加尺取。思路看到同余,立刻想到作差。我们建立差分数组\(c_i=|a_i-a_{i-1}|\),注意取了绝对值。此时,我们只需在\(c_i\)......
  • CF1715A 题解
    前言题目传送门!更好的阅读体验?赛时瞎胡了个结论,然后就过了。思路Megan从左下角到右上角,至少也得要\((n+m-1)\)步。于是考虑让Stanley少走几步。如图,容易......
  • CF1715C 题解
    前言题目传送门!更好的阅读体验?简单的数学题。思路每次只变一个数,因此考虑在短时间内计算:每个位置的数产生的贡献。容易发现以下的条件:不管\(a_i\)是什么,当它作......
  • CF1715B 题解
    前言题目传送门!更好的阅读体验?看起来挺难,其实一分钟就能想出来。思路首先考虑什么时候无解。由于\(k\times\left\lfloor\dfrac{a}{k}\right\rfloor\lea\le\le......