【2022 省选训练赛 Contest 18 A】B(Splay)
B
题目链接:2022 省选训练赛 Contest 18 A
题目大意
给你 n 个点,每个点第 i 天的代价是 b[i]+(i-1)a[i]。
然后要你在前 m 天每天选一个点,然后最小化总代价。
思路
首先考虑怎么 DP,观察到如果你选好的一个点集,你可以很容易的得到最优的选的方案。
即按 \(a_i\) 从大到小排,然后每次依次选,这样增长的量就会最小。
我们可以按 \(a_i\) 从大到小排序来 DP,设 \(f_{i,j}\) 为搞定前 \(i\) 个,选了 \(j\) 个的最小费用。
然后每次下一个选或者不选转移,显然超时。
考虑维护每个 \(f_i\) 数组,亦或者说是看一个新的数会产生什么。
然后有一些性质。
首先就是 \(f_{i,j}\) 的点集必定是 \(f_{i,j+1}\) 点集的子集,你可以用反证法来得到。
(反正意思就是你不选这个点集的肯定不如 \(f_{i,j}\) 的点集加上一个数更优)
然后接着就是加入一个数,它能修改的范围一定是一段后缀。
这个也是可以通过反证法得到,根据前面的性质就有了。
那我们就可以通过二分出贡献的后缀。
然后你要支持区间插入,区间加,区间二分,可以用平衡树来实现。
(过程就是维护每个点当前代表的选的个数,以及新选这个需要的费用)
(那你贡献就是一个区间加值,就是后面的一段都要加上你这个 \(a_i\),因为你每往后一个位置,就多了 \(a_i\) 的费用)
代码
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC target("avx")
#pragma GCC optimize("Ofast")
#pragma GCC optimize("inline")
#pragma GCC optimize("-fgcse")
#pragma GCC optimize("-fgcse-lm")
#pragma GCC optimize("-fipa-sra")
#pragma GCC optimize("-ftree-pre")
#pragma GCC optimize("-ftree-vrp")
#pragma GCC optimize("-fpeephole2")
#pragma GCC optimize("-ffast-math")
#pragma GCC optimize("-fsched-spec")
#pragma GCC optimize("unroll-loops")
#pragma GCC optimize("-falign-jumps")
#pragma GCC optimize("-falign-loops")
#pragma GCC optimize("-falign-labels")
#pragma GCC optimize("-fdevirtualize")
#pragma GCC optimize("-fcaller-saves")
#pragma GCC optimize("-fcrossjumping")
#pragma GCC optimize("-fthread-jumps")
#pragma GCC optimize("-funroll-loops")
#pragma GCC optimize("-fwhole-program")
#pragma GCC optimize("-freorder-blocks")
#pragma GCC optimize("-fschedule-insns")
#pragma GCC optimize("inline-functions")
#pragma GCC optimize("-ftree-tail-merge")
#pragma GCC optimize("-fschedule-insns2")
#pragma GCC optimize("-fstrict-aliasing")
#pragma GCC optimize("-fstrict-overflow")
#pragma GCC optimize("-falign-functions")
#pragma GCC optimize("-fcse-skip-blocks")
#pragma GCC optimize("-fcse-follow-jumps")
#pragma GCC optimize("-fsched-interblock")
#pragma GCC optimize("-fpartial-inlining")
#pragma GCC optimize("no-stack-protector")
#pragma GCC optimize("-freorder-functions")
#pragma GCC optimize("-findirect-inlining")
#pragma GCC optimize("-fhoist-adjacent-loads")
#pragma GCC optimize("-frerun-cse-after-loop")
#pragma GCC optimize("inline-small-functions")
#pragma GCC optimize("-finline-small-functions")
#pragma GCC optimize("-ftree-switch-conversion")
#pragma GCC optimize("-foptimize-sibling-calls")
#pragma GCC optimize("-fexpensive-optimizations")
#pragma GCC optimize("-funsafe-loop-optimizations")
#pragma GCC optimize("inline-functions-called-once")
#pragma GCC optimize("-fdelete-null-pointer-checks")
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const int N = 1e6 + 100;
struct node {
ll a, b;
}a[N];
int n, k, rt;
ll ans;
ll re; char c;
ll read() {
re = 0; c = getchar();
while (c < '0' || c > '9') c = getchar();
while (c >= '0' && c <= '9') {
re = (re << 3) + (re << 1) + c - '0';
c = getchar();
}
return re;
}
bool cmp(node x, node y) {
return x.a > y.a;
}
ll clac(int x, ll k) {
return (k - 1) * a[x].a + a[x].b;
}
struct SPLAY {
int tot, pl[N], ls[N], rs[N], lzyp[N], fa[N];
ll val[N], lzyv[N];
bool lrs(int x) {return ls[fa[x]] == x;}
int newpoint(int x, ll k) {
int now = ++tot;
val[now] = k; pl[now] = x;
return now;
}
void downv(int x, ll va) {
val[x] += va; lzyv[x] += va;
}
void downp(int x, int p) {
pl[x] += p; lzyp[x] += p;
}
void down(int now) {
if (lzyv[now]) {
if (ls[now]) downv(ls[now], lzyv[now]);
if (rs[now]) downv(rs[now], lzyv[now]);
lzyv[now] = 0;
}
if (lzyp[now]) {
if (ls[now]) downp(ls[now], lzyp[now]);
if (rs[now]) downp(rs[now], lzyp[now]);
lzyp[now] = 0;
}
}
void rotate(int x) {
int y = fa[x], z = fa[y];
int b = lrs(x) ? rs[x] : ls[x];
if (z) (lrs(y) ? ls[z] : rs[z]) = x;
if (lrs(x)) rs[x] = y, ls[y] = b;
else ls[x] = y, rs[y] = b;
fa[x] = z; fa[y] = x;
if (b) fa[b] = y;
}
void Splay(int x) {
while (fa[x]) {
if (fa[fa[x]]) {
if (lrs(x) == lrs(fa[x])) rotate(fa[x]);
else rotate(x);
}
rotate(x);
}
rt = x;
}
int get(int p) {
int now = rt, re = p;
while (now) {
down(now);
if (val[now] < clac(p, pl[now])) now = rs[now];
else re = pl[now], now = ls[now];
}
return re;
}
void insert(int x) {
int now = rt;
while (1) {
down(now);
int tmp = pl[now] < pl[x] ? rs[now] : ls[now];
if (!tmp) {
(pl[now] < pl[x] ? rs[now] : ls[now]) = x;
fa[x] = now; break;
}
else {
now = tmp;
}
}
Splay(x);
}
void count(int now) {
if (pl[now] <= k) ans += val[now];
down(now);
if (ls[now]) count(ls[now]);
if (rs[now]) count(rs[now]);
}
}T;
int main() {
n = read(); k = read();
for (int i = 1; i <= n; i++) a[i] = (node){read(), read()};
sort(a + 1, a + n + 1, cmp);
rt = T.newpoint(1, clac(1, 1));
for (int i = 2; i <= n; i++) {
int pl = T.get(i);
T.insert(T.newpoint(pl, clac(i, pl)));
if (T.rs[rt]) {
T.downp(T.rs[rt], 1);
T.downv(T.rs[rt], a[i].a);
}
}
T.count(rt);
printf("%lld", ans);
return 0;
}