CodeTON Round 5 (Div. 1 + Div. 2, Rated, Prizes!) E. Tenzing and Triangle

Problem Description

在一个二维平面上有 n n n 个不同的点和一条 x + y = k x+y=k x+y=k 的直线,对于每个点 ( x i , y i ) (x_i,y_i) (xi,yi) 都满足 0 ≤ x i , y i , x i + y i < k 0 \le x_i,y_i,x_i+y_i < k 0xi,yi,xi+yi<k

T e n z i n g Tenzing Tenzing 想要删除所有点,他可以进行以下两种操作:

  1. 画三角形:选择两个非负整数 a , b a,b a,b ,满足 a + b < k a+b<k a+b<k ,则以 x = a , y = b 和 x + y = k x=a,y=b和x+y=k x=a,y=bx+y=k 构成的三角形内的点都会被删除,设三角形边长为 l , l , 2 l l,l,\sqrt{2}l l,l,2 l ,那么将花费 l × A l \times A l×A
  2. 删除一个特定的点 i i i ,代价是 c i c_i ci

算出最小花费。

Input

第一行输入三个整数 n , k , A ( 1 ≤ n , k ≤ 2 × 1 0 5 , 1 ≤ A ≤ 1 0 4 ) n,k,A(1 \le n,k \le 2 \times 10^5,1\le A \le 10^4) n,k,A(1n,k2×105,1A104) ,表示点的个数,斜线 x + y = k x+y=k x+y=k 相关系数和画三角形的基础花费的代价。

接下来 n n n 行每行输入三个整数 x i , y i , c i ( 0 ≤ x i , y i , x i + y i < k , 1 ≤ c i ≤ 1 0 4 ) x_i,y_i,c_i(0 \le x_i,y_i,x_i+y_i <k,1 \le c_i \le 10^4) xi,yi,ci(0xi,yi,xi+yi<k,1ci104) ,表示坐标和单个点删除的代价。

Output

输出删去所有点的最小花费。

Solution

对于两个重叠的三角形,我们会发现左图花费更大覆盖面积更小,右图花费更小覆盖面积更大,那么有个很显然的结论是三角形删点操作是相互分离、独立的,由此我们便考虑如何去 d p dp dp

![[Pasted image 20230825113257.png]]![[Pasted image 20230825113338.png]]

我们设 d p i dp_i dpi 表示为删除 y ≤ i y \le i yi 的所有点的最小花费。

![[Pasted image 20230825115630.png]]

那么我们就可以得到一个比较暴力的状态转移:

d p i = min ⁡ j ∈ [ 0 , i − 1 ] ( d p i − 1 + ∑ p o s y = i c p o s    ,    d p j + A ( i − j ) + ∑ p o s ∈ V 2 c p o s ) dp_i = \min\limits_{j \in [0,i-1]}(dp_{i-1}+\sum\limits_{pos_y=i}c_{pos} \;,\;dp_j+A(i-j)+\sum\limits_{pos \in V_2}c_{pos}) dpi=j[0,i1]min(dpi1+posy=icpos,dpj+A(ij)+posV2cpos)
对于 min ⁡ \min min 的左部中的 ∑ p o s y = i c p o s \sum\limits_{pos_y=i}c_{pos} posy=icpos ,只需在遍历到 i i i 时对 p o s y = i pos_y=i posy=i 这一层的点权简单地求一遍和就行,整体复杂度是只有 O ( n ) O(n) O(n)

而对于右侧,这个是一个区间求最小值问题,那么此时我们便可以考虑是否可以改动公式来支持快速区间询问,以下给出一种改动方案:
d p j + A ( i − j ) + ∑ p o s ∈ V 2 c p o s → ( d p j − A ⋅ j + ∑ p o s ∈ V 2 c p o s ) + A ⋅ i dp_j+A(i-j)+\sum\limits_{pos \in V_2}c_{pos} \to (dp_j-A·j+\sum\limits_{pos \in V_2}c_{pos})+A·i dpj+A(ij)+posV2cpos(dpjAj+posV2cpos)+Ai
我们先只考虑 d p j − A ⋅ j dp_j-A·j dpjAj ,其实我们在每次求完 d p j dp_j dpj 时便能在线段树的 j j j 位置加上 d p j − A ⋅ j dp_j-A·j dpjAj ,那么在遍历到 i i i 时,只需 q u e r y m i n ( 0 , i − 1 ) query_{min}(0,i-1) querymin(0,i1) 即可,那么现在的问题就出现在了如何处理 ∑ p o s ∈ V 2 c p o s \sum\limits_{pos \in V_2}c_{pos} posV2cpos

![[Pasted image 20230826014158.png]]

如上图所示,当我们的 j j j L 1 L_1 L1 的范围内取时,点 p p p 都是属于 ∑ p o s ∈ V 2 c p o s \sum\limits_{pos \in V_2}c_{pos} posV2cpos 的,那么此时我们就能很容易地想到,我们只需当遍历到 i = = y p i==y_p i==yp 时在线段树上的 ( x p , i ] (x_p,i] (xp,i] 都加上 c p c_p cp ,而此处有两点值得注意:

  1. ( x p , i ] (x_p,i] (xp,i] 左端取开区间是由于当取到 j = = x p j==x_p j==xp 时,点 p p p 此时是所做 V 3 V_3 V3 的一部分,所以不应当加上 c p c_p cp
  2. 当我们算完第 i i i d p i dp_i dpi 时,应当在线段树上的 i i i 位置加上 d p i − A ⋅ i − ∑ p o s y = i c p o s dp_i-A·i-\sum\limits_{pos_y=i}c_{pos} dpiAiposy=icpos ,这是由于 d p i dp_i dpi 表示的是删除 y ≤ i y \le i yi 的所有点的最小花费, ∑ p o s y = i c p o s \sum\limits_{pos_y=i}c_{pos} posy=icpos 已经包含在 d p i dp_i dpi 中,但是在线段树中又在 i i i 位置上了,所以此处要减去以免重复计算。

最终时间复杂度为 O ( ( n + k ) l o g k ) O((n+k)logk) O((n+k)logk)

Code

#include <bits/stdc++.h>
#define endl '\n'
#define lowbit(x) x &-x
using namespace std;
const int N = 2e5 + 10;
int n, k, A;
struct node {
	int l, r;
	int val, delta;
} tr[4 * N], *ls, *rs;
inline void pushdown(int u) {
	if (tr[u].delta) {
		ls = &tr[u << 1], rs = &tr[u << 1 | 1];
		ls->val += tr[u].delta, ls->delta += tr[u].delta;
		rs->val += tr[u].delta, rs->delta += tr[u].delta;
		tr[u].delta = 0;
	}
}
inline void pushup(int u) {
	tr[u].val = min(tr[u << 1].val, tr[u << 1 | 1].val);
}
void build(int u, int l, int r) {
	tr[u] = {l, r, 0};
	if (l == r) return;
	int mid = l + r >> 1;
	build(u << 1, l, mid);
	build(u << 1 | 1, mid + 1, r);
	pushup(u);
}
void modify(int u, int l, int r, int x) {
	if (tr[u].r < l || tr[u].l > r)return;
	if (l <= tr[u].l && tr[u].r <= r) {
		tr[u].delta += x;
		tr[u].val += x;
		return;
	}
	pushdown(u);
	modify(u << 1, l, r, x);
	modify(u << 1 | 1, l, r, x);
	pushup(u);
}
int query(int u, int l, int r) {
	if (tr[u].l > r || tr[u].r < l)return 2e9;
	if (l <= tr[u].l && tr[u].r <= r) return tr[u].val;
	pushdown(u);
	int L = query(u << 1, l, r);
	int R = query(u << 1 | 1, l, r);
	return min(L, R);
}
vector<pair<int, int>>cor[N];
signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin >> n >> k >> A;
	build(1, 0, k);
	for (int i = 1; i <= n; i++) {
		int x, y, c;
		cin >> x >> y >> c;
		cor[y].push_back({x, c});
	}
	vector<int>dp(k + 1);
	for (int i = 1; i <= k; i++) {
		int sum = 0;
		for (auto [x, c] : cor[k - i]) {
			sum += c;
			modify(1, x + 1, i, c);
		}
		dp[i] = min(dp[i - 1] + sum, query(1, 0, i - 1) + A * i);
		modify(1, i, i, dp[i] - A * i - sum);
	}
	cout << dp[k] << endl;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值