题解:CF1787H Codeforces Scoreboard
posted on 2024-12-17 06:07:30 | under | source
厉害题啊。
首先考虑朴素 dp,首先放宽限制把 \(\max\) 丢掉,允许任选一个。相当于钦定了一些题的价值为 \(b-kt\),显然将它们按 \(k\) 从大到小排在前面做,其它的丢到后面。
按 \(k\) 大到小排个序,记 \(f_{i,j}\) 为前 \(i\) 题钦定了 \(j\) 题的最大价值,转移:
好的现在有了 \(O(n^2)\) 做法。上述转移有点难受,同时出现了 \(a,b\),而且这个 \(-k\) 看的也不太顺眼。不妨修改定义为最小罚时,记 \(c_i=b_i-a_i\),转移:
特殊的,\(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}\) 是极小值。
代入原转移式并化简:
作差得到 \(g\) 转移式:
然后有结论:
也就是说 \(\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;
}

浙公网安备 33010602011771号