题解:CF1787H Codeforces Scoreboard

posted on 2024-12-17 06:07:30 | under | source

厉害题啊。

首先考虑朴素 dp,首先放宽限制把 \(\max\) 丢掉,允许任选一个。相当于钦定了一些题的价值为 \(b-kt\),显然将它们按 \(k\) 从大到小排在前面做,其它的丢到后面。

\(k\) 大到小排个序,记 \(f_{i,j}\) 为前 \(i\) 题钦定了 \(j\) 题的最大价值,转移:

\[f_{i,j}= \max(f_{i-1,j}+a_i,f_{i-1,j-1}+b_i-k_i\times j) \]

好的现在有了 \(O(n^2)\) 做法。上述转移有点难受,同时出现了 \(a,b\),而且这个 \(-k\) 看的也不太顺眼。不妨修改定义为最小罚时,记 \(c_i=b_i-a_i\),转移:

\[f_{i,j}= \min(f_{i-1,j}+c_i,f_{i-1,j-1}+k_i\times j) \]

特殊的,\(j=0\) 不能有后面那条转移。为了同一转移形式,我们将 \(f_{i,-1}\) 设为 \(inf\),这样就无需特判了。

\(k_i\times j\) 联想到一次函数,若将 \(f_i\) 视为自变量为 \(j\) 的函数,那么 \(f_{i-1}\to f_i\) 实际上就是函数平移、函数再加上个斜率为 \(k_i\) 的直线中取 \(\min\)。而且打个表可以还可发现 \(f_i\) 是凸包。

于是考虑 slope trick,做差分 \(g_{i,j}=f_{i,j}-f_{i,j-1}\)。接下来仅考虑 \(j\ge 1\) 时的转移,而边界 \(g_{i,0}\) 是极小值。

代入原转移式并化简:

\[f_{i,0}+\sum\limits_{k=1}^j g_{i,j}=f_{i-1,0}+\sum\limits_{k=1}^{j-1}g_{i-1,k}+\min(g_{i-1,j}+c_i,k_i\times j) \]

作差得到 \(g\) 转移式:

\[g_{i,j}=\sum\limits_{k=0}^j g_{i,k}-\sum\limits_{k=0}^{j-1} g_{i,k}=g_{i-1,j-1}+\min(g_{i-1,j}+c_i,k_i\times j)-\min(g_{i-1,j-1}+c_i,k_i\times (j-1)) \]

然后有结论:

\[\forall j\ge 1,g_{i,j}-g_{i,j-1}\ge k_i \]

也就是说 \(\min\) 的左边比右边增长快,那么就能通过二分去掉 \(\min\) 了。怎么证明?显然 \(j=1\)\(g_{i,1}-g_{i,0}\ge k_i\)。考虑 \(j>1\) 的部分,施归纳法,已知 \(g_{i-1}\) 成立。

首先对第一个 \(\min\) 二分找到 \(x\) 满足 \(g_{i-1,x}+c_i\le k_i\times x\),且 \(g_{i-1,x+1}+c_i> k_i\times (x+1)\)。注意到转移中的两个 \(\min\) 的相似性,那么 \(x+1\) 即为第二个 \(\min\) 的分界处。

(注意一下,对 \(g_{i-1}\) 而言,\(j=0\) 肯定左边较小;\(j=i\) 肯定右边较小,因为 \(g_{i-1,i}=inf\)。所以不用担心边界之类的问题。)

将转移分为三段:

  • \(j\le x\)\(g_{i,j}=g_{i-1,j}\)

  • \(j=x+1\)\(g_{i,j}=k_i\times (x+1)-c_i\)

  • \(j> x+1\)\(g_{i,j}=g_{i-1,j-1}+k_i\)

可以形象地看成:先继承 \(g_{i-1}\),然后对于 \(j>x\) 的部分 \(+k_i\),最后在 \(x\) 后面插入一个 \(k_i\times (x+1)-c_i\)

那么单调性如何呢?

  • \(2\le j\le x\)\(g_{i,j}-g_{i,j-1}=g_{i-1,j}-g_{i-1,j-1}\ge k_{i-1}\ge k_i\)

  • \(j>x+2\)\(g_{i,j}-g_{i,j-1}=g_{i-1,j-1}-g_{i-1,j-2}\ge k_{i-1}\ge k_i\)

  • \(j=x+1\)\(g_{i,x+1}-g_{i,x}=k_i\times (x+1)-c_i-g_{i-1,x}\)。由 \(x\) 定义知 \(g_{i-1,x}+c_i\le k_i\times x\),即 \(k_i\times x-c_i-g_{i-1,x}\ge 0\),两边同时加上 \(k_i\) 即可得证。

  • \(j=x+2\)\(g_{i,x+2}-g_{i,x+1}=g_{i-1,x+1}-k_i\times (x+1)+c_i\)。同上由定义 \(g_{i-1,x+1}-k\times (x+1)-c_i\ge 0\) 即可得证。

所以结论成立。可以使用平衡树维护 \(g\),复杂度 \(O(n\log n)\)

代码

用 fhq treap 写的,注意最后算答案时不要漏掉 \(f_{n,0}\)

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

#define int long long
#define MAX(a, b) a = max(a, b)
const int N = 3e5 + 5, inf = 1e16;
int T, n, ans, sb;
struct node{int a, b, k;} p[N];

namespace FHR{
	struct node{int ls, rs, s, siz, tag, rnd;} t[N];
	int root, tot;
	
	inline void init(int len){
		random_device rd;
		int seed = rd();
		mt19937 engine(seed);
		root = tot = 0;
		for(int i = 0; i <= len; ++i) t[i] = {0, 0, 0, 0, 0, engine()};
	}
	inline int newnode(int x) {++tot, t[tot].s = x; t[tot].siz = 1; return tot;}
	inline void addtag(int u, int x) {if(!u) return ; t[u].s += x, t[u].tag += x;}
	inline void psup(int u) {if(!u) return ; t[u].siz = t[t[u].ls].siz + t[t[u].rs].siz + 1;}
	inline void psdw(int u) {if(!u) return ; addtag(t[u].ls, t[u].tag), addtag(t[u].rs, t[u].tag), t[u].tag = 0;}
	inline void split(int u, int &x, int &y, int pos){
		psdw(u);
		if(!u) {x = y = 0; return ;}
		int lsiz = t[t[u].ls].siz;
		if(lsiz + 1 <= pos) x = u, split(t[u].rs, t[u].rs, y, pos - lsiz - 1);
		else y = u, split(t[u].ls, x, t[u].ls, pos);
		psup(x), psup(y);
	}
	inline int mer(int x, int y){
		psdw(x), psdw(y);	
		if(!x || !y) return x + y;
		if(t[x].rnd < t[y].rnd) {t[x].rs = mer(t[x].rs, y); psup(x); return x;}
		else {t[y].ls = mer(x, t[y].ls); psup(y); return y;}
	}
	inline int fid(int u, int s, int k, int c){
		psdw(u);
		if(!u) return 0;
		int lsiz = t[t[u].ls].siz, res = 0;
		if(t[u].s + c <= (s + lsiz + 1) * k) res = max(s + lsiz + 1, fid(t[u].rs, s + lsiz + 1, k, c));
		else res = fid(t[u].ls, s, k, c);
		psup(u);
		return res;
	}
	inline void upd(int l, int r, int x){
		if(l > r) return ;
		int a, b, c, d;
		split(root, a, d, r);
		split(a, b, c, l - 1);
		addtag(c, x);
		a = mer(b, c), root = mer(a, d);
	} 
	inline void ins(int pos, int x){
		if(pos < 0 || pos > tot) return ;
		int a, b, c = newnode(x);
		split(root, a, b, pos);
		root = mer(mer(a, c), b);
	}
	inline void debug(int u){
		psdw(u);
		if(!u) return ;
		debug(t[u].ls);
		cout << t[u].s << ' ';
		debug(t[u].rs);
	}
	inline void getans(int u, int sum, int &mi){
		psdw(u); 
		if(!u) return ;
		getans(t[u].ls, sum, mi);
		mi = min(mi, sum + t[t[u].ls].s + t[u].s);
		getans(t[u].rs, sum + t[u].s + t[t[u].ls].s, mi);
		t[u].s += t[t[u].ls].s + t[t[u].rs].s;
	}
}
signed main(){
	
	cin >> T;
	for(int tt = 1; tt <= T; ++tt){
		scanf("%lld", &n), sb = ans = 0;
		for(int i = 1; i <= n; ++i) scanf("%lld%lld%lld", &p[i].k, &p[i].b, &p[i].a), p[i].b -= p[i].a, sb += p[i].a;
		sort(p + 1, p + 1 + n, [](node A, node B){return A.k > B.k;});
		FHR::init(n + 5);
		for(int i = 1; i <= n; ++i){
			int pos = FHR::fid(FHR::root, 0, p[i].k, p[i].b);
			FHR::upd(pos + 1, i - 1, p[i].k);
			FHR::ins(pos, p[i].k * (pos + 1) - p[i].b);
		}
//		FHR::debug(FHR::root);  
		FHR::getans(FHR::root, 0, ans);
		printf("%lld\n", sb - ans);
	}
	return 0;
}
posted @ 2026-01-15 08:16  Zwi  阅读(3)  评论(0)    收藏  举报