CF1787H Codeforces Scoreboard

CF1787H Codeforces Scoreboard

校内测试的一道题, 考试时根本没动。。
题面
考虑 k 比较大的放前面肯定优, 然后修门挨着放也肯定优, 所以先按 k 排个序, 然后我们就只考虑每个门修不修。
设计状态 f[i][j] 表示前 i 个点, 有 j 个门取 bkt, 少送回去的最少人数。

c[i] 表示 b[i]a[i]

f[i][j]={f[i1][j]+c[i]j=0min{f[i1][j]+c[i],f[i1][j1]+k[i]×j}j>0$

时间复杂度 O(n2), 这里不考虑 k[i]×j>c[i], 虽然有点不严谨, 但是不影响答案, 因为它肯定比直接取 c[i] 更劣, 而且还方便后面的优化。

这里先人类智慧的感受一下, j 大了, 那么后面的损失就会'越来越大'(此处不严谨), 太少了, 也不行, 所以要在中间某个位置, 取最合适, 似乎可以感受到这个 f[i] 是下凸的, 再打表看一看, 发现确实是, 所以考虑一下怎么优化。

因为要一个一个取min, 所以我们预估的优化就是, 可以通过神奇方法, 批量处理, 也就是在某个区间我们清楚的知道谁大谁小。 那么就考虑这个函数有什么性质, 下凸的, 也就是差分数组单增。
所以就有

f[i][j]=c[i]+k=1jg[i][k]

g[i][j]=g[i1][j1]+min(g[i1][j]+c[i],k[i]×j)min(g[i1][j1]+c[i],k[i]×(j1))

再仔细观察, k[i]×jk[i]×(j1) 差了 k[i], 可以猜测加归纳加分讨, g[i - 1][j] 的增速大于 k[i] 的增速, 也就是这两个函数只有至多一个交点, 找到这个交点就可以直接得到他们的大小关系。

所以我们维护 g 数组, 用平衡树维护, 插入一个点, 后缀加。 找这个交点可以二分, 但考虑如果我们使用 FHQtreap, 那么他自带分治结构, split的时候就可以直接二分找到交点了, 所以用 FHQtreap 维护即可做到 O(nlogn) 时间复杂度。

最后答案就是 b[i]min(f[n][j]), 也就是 g 数组的最小前缀和, 直接取所有的负数即可, 因为 g 数组单增。

代码

#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
#define int long long
struct FHQ{
	int ch[N][2], val[N], key[N], siz[N], add[N], rt, tot;
	#define ls(u) ch[u][0]
	#define rs(u) ch[u][1]
	void clear() {
		for (int i = 1; i <= tot; i++) 
			ls(i) = rs(i) = val[i] = key[i] = siz[i] = add[i] = rt = 0;
		tot = 0;
	}
	void NewNode(int v) { ++tot; val[tot] = v; siz[tot] = 1; key[tot] = rand(); }
	void pushdown(int u) {
		if (add[u]) {
			if (ls(u)) val[ls(u)] += add[u], add[ls(u)] += add[u];
			if (rs(u)) val[rs(u)] += add[u], add[rs(u)] += add[u];
			add[u] = 0; 
		}
	}
	void pushup(int u) { siz[u] = siz[ls(u)] + siz[rs(u)] + 1;	}
	void split(int u, int &L, int &R, int lft, int k, int c) {
		if (!u) return L = R = 0, void();
		pushdown(u);
		if (val[u] + c > (lft + siz[ls(u)] + 1) * k) 
			split(ls(u), L, ls(R = u), lft, k, c);
		else split(rs(u), rs(L = u), R, lft + siz[ls(u)] + 1, k, c);
		pushup(u);
	}
	int merge(int L, int R) {
		if (!L || !R) return L + R;
		pushdown(L); pushdown(R);
		if (key[L] > key[R]) 
			return rs(L) = merge(rs(L), R), pushup(L), L;
		else 
			return ls(R) = merge(L, ls(R)), pushup(R), R;
	}
	int qry(int u) {
		if (!u) return 0;
		pushdown(u);
		return (val[u] < 0 ? val[u] : 0) + qry(ls(u)) + qry(rs(u));
	}
}T; 
int t, n, sum;
struct dr{ 
	int k, a, b, c;
	bool operator < (const dr &x) const {
		return k > x.k;
	}
}a[N];
signed main() {
//	freopen("faded.in", "r", stdin);
//	freopen("faded.out", "w", stdout);	
	srand(time(NULL));
	scanf("%lld", &t);
	while (t--) {
		scanf("%lld", &n); 
		sum = 0; T.clear();
		for (int i = 1; i <= n; i++) 
			scanf("%lld%lld%lld", &a[i].k, &a[i].b, &a[i].a), 
			sum += a[i].b, a[i].c = a[i].b - a[i].a;
		sort(a + 1, a + 1 + n);
		for (int i = 1; i <= n; i++) {
			int L, R;
			sum -= a[i].c;
			T.split(T.rt, L, R, 0, a[i].k, a[i].c);
			T.NewNode((T.siz[L] + 1) * a[i].k - a[i].c);
			T.val[R] += a[i].k, T.add[R] += a[i].k;
			T.rt = T.merge(L, T.merge(T.tot, R));
		}
		printf("%lld\n", sum - T.qry(T.rt));
	}
	return 0;
}
posted @   qqrj  阅读(29)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 如何调用 DeepSeek 的自然语言处理 API 接口并集成到在线客服系统
· 【译】Visual Studio 中新的强大生产力特性
· 2025年我用 Compose 写了一个 Todo App
点击右上角即可分享
微信分享提示