【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;
}
posted @ 2022-03-25 22:00  あおいSakura  阅读(26)  评论(0编辑  收藏  举报