CF2145E Predicting Popularity 题解

题目传送门

思路

\(k_i = \max(0,a_i - ac) + \max(0, d_i - dr)\)。考虑用 \(cnt_j\) 存满足 \(k_i = j\)\(i\) 的个数。我们发现如果要满足最大化看电影的人数,需要找到一个最大的 \(j\),满足:

\[\begin{aligned} \forall 1 \le i \le j,\; \sum_{k=1}^i cnt_k \ge i \end{aligned} \]

移项得:

\[\begin{aligned} \forall 1 \le i \le j,\; \sum_{k=1}^i cnt_k - i \ge 0 \end{aligned} \]

这用另外的语言书写即为:

\[\begin{aligned} \min_{1 \le i \le j} (\sum_{k=1}^i cnt_k - i) \ge 0 \end{aligned} \]

这就是一个经典的线段树维护区间 \(\min\)。这里有一个性质:如果 \(k_i > n\),等价于 \(k_i = n + 1\),这样就可以把线段树开在值域下了。每次修改时就将 \([old_k, n + 1]\) 这段区间 \(-1\),再将 \([new_k, n + 1]\) 这段区间 \(+1\),直接加一个懒标记即可。查询时线段树二分,如果 \([l,mid]\) 中的最小值 \(\ge 0\),则往右跳,否则往左跳。注意往右跳时需要特判,因为 \(mid\) 一定可行。时间复杂度 \(\mathcal{O}(n \log n)\),可以通过此题。

代码

#include <bits/stdc++.h>
#define int long long
using namespace std;

const int N = 5e5 + 5;

int ac, dr, n, m, len = 1000000;
int a[N], d[N], k[N], cnt[N], f[N];

struct seg_tree
{
	struct node
	{
		int l, r, minn, lazy;
	} tr[4 * N];
	void push_up(int p)
	{
		tr[p].minn = min(tr[p * 2].minn, tr[p * 2 + 1].minn);
	}
	void push_down(int p)
	{
		if (tr[p].lazy)
		{
			tr[p * 2].lazy += tr[p].lazy, tr[p * 2 + 1].lazy += tr[p].lazy;
			tr[p * 2].minn += tr[p].lazy, tr[p * 2 + 1].minn += tr[p].lazy;
			tr[p].lazy = 0;
		}
	}
	void build(int p, int l, int r)
	{
		tr[p] = {l, r, (int)1e18, 0};
		if (l == r)
		{
			tr[p].minn = min(tr[p].minn, f[l]);
			return;
		}
		int mid = (l + r) >> 1;
		build(p * 2, l, mid);
		build(p * 2 + 1, mid + 1, r);
		push_up(p);
	}
	void modify(int p, int l, int r, int x)
	{
		if (l <= tr[p].l && tr[p].r <= r)
		{
			tr[p].lazy += x;
			tr[p].minn += x;
			return;
		}
		push_down(p);
		int mid = (tr[p].l + tr[p].r) >> 1;
		if (l <= mid) modify(p * 2, l, r, x);
		if (r > mid) modify(p * 2 + 1, l, r, x);
		push_up(p);
	}
	int query(int p)
	{
		if (tr[p].l == tr[p].r)
		{
			if (tr[p].minn >= 0) return tr[p].l;
			return 0;
		}
		push_down(p);
		int mid = (tr[p].l + tr[p].r) >> 1;
		if (tr[p * 2].minn >= 0) return max(mid, query(p * 2 + 1));
		return query(p * 2);
	}
} ST;

signed main()
{
	scanf("%lld%lld%lld", &ac, &dr, &n);
	for (int i = 1; i <= n; i++)
		scanf("%lld", &a[i]);
	for (int i = 1; i <= n; i++)
		scanf("%lld", &d[i]);
	for (int i = 1; i <= n; i++)
	{
		k[i] = min(max(a[i] - ac, 0ll) + max(d[i] - dr, 0ll), n + 1);
		cnt[k[i]]++;
	}
    int sum = 0;
    for (int i = 0; i <= n + 2; i++)
    {
    	f[i] = sum - i;
		sum += cnt[i];
    }
	scanf("%lld", &m);
	ST.build(1, 0, n + 2);
	while (m--)
	{
		int id, na, nd;
		scanf("%lld%lld%lld", &id, &na, &nd);
		int nowk = max(na - ac, 0ll) + max(nd - dr, 0ll);
		ST.modify(1, min(k[id], n + 1) + 1, n + 2, -1);
		ST.modify(1, min(nowk, n + 1) + 1, n + 2, 1);
		k[id] = nowk;
		printf("%lld\n", ST.query(1));
	}
	return 0;
}
posted @ 2025-10-19 10:55  lucasincyber  阅读(1)  评论(0)    收藏  举报