首页 > 其他分享 >CF1787H Codeforces Scoreboard

CF1787H Codeforces Scoreboard

时间:2024-05-10 11:45:56浏览次数:22  
标签:CF1787H return rs int min Scoreboard Codeforces times ls

CF1787H Codeforces Scoreboard

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

设 \(c[i]\) 表示 \(b[i] - a[i]\)。

\[f[i][j] = \left\{ \begin{matrix} f[i - 1][j] + c[i] \quad j = 0 \\ min \{f[i - 1][j] + c[i], f[i - 1][j - 1] + k[i] \times j\} \quad j > 0 $ \end{matrix} \right. \]

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

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

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

\(f[i][j] = c[i] + \sum_{k = 1}^jg[i][k]\)

$g[i][j] = 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)) $

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

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

最后答案就是 \(\sum 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;
}

标签:CF1787H,return,rs,int,min,Scoreboard,Codeforces,times,ls
From: https://www.cnblogs.com/qerrj/p/18183985

相关文章

  • CodeForces 1967D Long Way to be Non-decreasing 题解
    题意简述yzh喜欢单调不降序列。她有一个序列\(a\),最初为\(a_1,\ldots,a_n\),其中每个元素都在\([1,m]\)内。她希望使序列变得单调不降,为此,她有一个序列$b_1\ldotsb_m$,每个元素也在\([1,m]\)内。她可以进行若干次操作,一次操作定义为:选择一个集合\(S\subseteq......
  • Codeforces Round 942 (Div. 2) D2
    链接题目要求用数学一点的形式表达出来就是统计有多少a,b满足1.\(1\leqa\leqn,1\leqb\leqm\)2.\(\existsk\inN^*,使得b\timesgcd(a,b)=k\times(a+b)\)首先,把a和b改写,使得\(gcd(a,b)\)消失\(a=q*d,b=p*d\),则,\(gcd(a,b)=d\)条件二变为:\(p\timesd^2=k\times(q\t......
  • Codeforces Round 941 (Div. 2) Div 1 cf941 A-D
     A感觉A比较复杂啊,相比较B简单明了。way1只要有相同的一个数,它的数目大于等于k,那么它就可以进行一次操作,接下来可以再摸k-1个数,那么对于无法凑成k个数的数字来说,无论现在它有多少个数(>=1),加上这k-1个数,都能凑成数目>=k。同时,这个操作可以无限循环下去。所以这道题的出题设......
  • 『Solution』Codeforces 372D Choosing Subtree is Fun
    首先这个\(k\)的限制不是很好入手,考虑先从如果选取了区间\([l,r]\)来入手。那么此时连通块最小的\(siz\)就是把这些点拎出来建虚树对应在原树上的所有点。那么这个有个结论,考虑按\(\operatorname{dfn}\)序排序后的点为\(p_0\simp_{k-1}\),那么对应的最小\(sz\)就......
  • 『Solution』Codeforces 1970B Exact Neighbours
    Easy没什么启发性,直接考虑Medium。考虑到\(a_1=0\),那么\(1\)明显直接和自己配对就行,考虑分配到一个特殊的位置\((1,1)\)。接下来考虑如果还有\(a_i=0\),那么明显\(i\)也是和自己配对,此时因为这是无关紧要的就可以离特殊的\((1,1)\)尽量远一点,也就是让\(x\)坐......
  • Codeforces Round 943 (Div. 3)
    CodeforcesRound943(Div.3)A.Maximize?题意:给定x,求一个范围在[1,x)的数字y,内使得gcd(x,y)+y最大,输出任意的y思路:数据范围很小,暴力枚举即可voidsolve(){intx;cin>>x;inty=1,ma=0;for(inti=1;i<x;i++){intres=__gc......
  • Codeforces 1129E Legendary Tree
    考虑让选取的集合更加特殊,不妨就让\(S=\{x\}\)。那么这个时候能发现询问\((S=\{x\},T,v)\)得到的就是以\(x\)为根时\(v\)的子树内\(T\)中的点的数量。考虑定个根,不妨为\(1\),同时令\(S=\{1\}\)。那么询问\((\{1\},\{1,\cdotsn\}\backslash\{1,x\},x)......
  • Codeforces Round 943 (Div. 3)
    无伤但没速通,然后被hack两个题,直接从rk90掉到rk114514+了,我是真他妈的服了。特此纪念A暴力枚举秒了。B二分答案秒了C他妈脑子抽了,差一步完全整解。我们发现只要确定\(a_{\mathbf{1}}\)那么你直接不断加\(x_i\)就能求出\(a_i\),但是直接这么搞过不了样例,观察一下,然后直......
  • Codeforces 452F Permutation
    对于\(a,b,\frac{a+b}{2}\)肯定是需要固定下一些变量来考虑的。考虑固定下\(c=\frac{a+b}{2}\),考虑\(a,b\)。那么这样的\(a,b\)肯定满足\(a-c=b-c,a\not=b\),那么以\(c\)为中心,\(a,b\)就是对称的。用\(s_i=0,1\)来表示\(i\)是在\(c\)的......
  • Codeforces 1044F DFS
    考虑到存在方案使得以\(u\)为起点还能走出原树的条件。能发现是以\(u\)为根,在原树上新添加的边只会是返祖边而不会有横叉边。这是因为如果有横叉边就肯定会在遍历到一边的点后先通过这条边走到另一边的点,就算上了这条边就不是原树了。那么考虑\((x,y)\),合法的\(u\)需要......