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;
}

浙公网安备 33010602011771号