← 全部文章

CF643G Choosing Ads 题解

本文目录(3 节)
  1. 题意
  2. 题解
  3. 代码

CF643G Choosing Ads

题意

  • 给定一个长度为 nn 的序列和一个整数 pp。
  • 有 mm 个操作,操作要么是区间赋值,要么是询问区间内出现次数至少占 p%p\% 的数。
  • 输出询问的答案时,可以包含错的数,也可以重复输出,但对的数一定要在答案中,且输出的数的个数不超过 ⌊100p⌋\lfloor \frac{100}{p} \rfloor。
  • n,m≤1.5×105n,m \le 1.5 \times 10^5,20≤p≤10020 \le p \le 100。

题解

首先考虑 p>50p > 50 的情况,问题变成区间绝对众数。

对于这个问题,我们可以每次同时删除两个不同的数,则最后剩下的互不相同的数最多只有一个,这一个就是答案。

扩展到 p≤50p \le 50 的情况也是这样的,我们每次同时删除 ⌊100p⌋+1\lfloor \frac{100}{p} \rfloor + 1 个互不相同的数,则最后剩下的互不相同的数最多只有 ⌊100p⌋\lfloor \frac{100}{p} \rfloor 个,这些数一定包含了所有答案。

由于 p≥20p \ge 20,因此 ⌊100p⌋≤5\lfloor \frac{100}{p} \rfloor \le 5,合并的部分可以当成常数。

考虑线段树维护序列,时间复杂度 O((n+m)log⁡n)\mathcal O((n+m) \log n)。

代码

const int N = 1.5e5 + 7;
int n, m, p, k, a[N];
struct T {
	int l, r, z;
	vector<pi> c;
} t[N<<2];

inline vector<pi> merge(vector<pi> a, vector<pi> b) {
	for (pi o : b) {
		bool ok = 0;
		for (pi &p : a)
			if (p.fi == o.fi) {
				p.se += o.se, ok = 1;
				break;
			}
		if (ok) continue;
		a.pb(o);
		if ((int)a.size() <= k) continue;
		int x = n;
		for (pi p : a) x = min(x, p.se);
		vector<pi> c;
		for (pi p : a)
			if (p.se - x) c.pb(mp(p.fi, p.se - x));
		a = c;
	}
	return a;
}

void build(int p, int l, int r) {
	t[p].l = l, t[p].r = r;
	if (t[p].l == t[p].r) return t[p].c.pb(mp(a[l], 1)), void();
	build(ls, l, md), build(rs, md + 1, r);
	t[p].c = merge(t[ls].c, t[rs].c);
}

inline void setx(int p, int x) {
	t[p].z = x, t[p].c.clear(), t[p].c.pb(mp(x, t[p].r - t[p].l + 1));
}

inline void spd(int p) {
	if (t[p].z) setx(ls, t[p].z), setx(rs, t[p].z), t[p].z = 0;
}

void change(int p, int l, int r, int x) {
	if (t[p].l >= l && t[p].r <= r) return setx(p, x);
	spd(p);
	if (l <= md) change(ls, l, r, x);
	if (r > md) change(rs, l, r, x);
	t[p].c = merge(t[ls].c, t[rs].c);
}

vector<pi> ask(int p, int l, int r) {
	if (t[p].l >= l && t[p].r <= r) return t[p].c;
	spd(p);
	vector<pi> c;
	if (l <= md) c = ask(ls, l, r);
	if (r > md) c = merge(c, ask(rs, l, r));
	return c;
}

int main() {
	rd(n), rd(m), rd(p), k = 100 / p, rda(a, n);
	build(1, 1, n);
	for (int i = 1, o, l, r, x; i <= m; i++) {
		rd(o), rd(l), rd(r);
		if (o == 1) rd(x), change(1, l, r, x);
		else {
			auto ans = ask(1, l, r);
			print(ans.size(), ' ');
			for (pi o : ans) print(o.fi, ' ');
			prints("");
		}
	}
	return 0;
}

标签:线段树、贪心

评论

    填写昵称即可留言。评论经站主审核后公开,请勿提交私人信息。

    正在连接评论服务…