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

Problem Description

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

\(Tenzing\) 想要删除所有点,他可以进行以下两种操作:

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

算出最小花费。

Input

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

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

Output

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

Solution

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

我们设 \(dp_i\) 表示为删除 \(y \le i\) 的所有点的最小花费。

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

\[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}) \]

对于 \(\min\) 的左部中的 \(\sum\limits_{pos_y=i}c_{pos}\) ,只需在遍历到 \(i\) 时对 \(pos_y=i\) 这一层的点权简单地求一遍和就行,整体复杂度是只有 \(O(n)\)

而对于右侧,这个是一个区间求最小值问题,那么此时我们便可以考虑是否可以改动公式来支持快速区间询问,以下给出一种改动方案:

\[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 \]

我们先只考虑 \(dp_j-A·j\) ,其实我们在每次求完 \(dp_j\) 时便能在线段树的 \(j\) 位置加上 \(dp_j-A·j\) ,那么在遍历到 \(i\) 时,只需 \(query_{min}(0,i-1)\) 即可,那么现在的问题就出现在了如何处理 \(\sum\limits_{pos \in V_2}c_{pos}\)

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

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

最终时间复杂度为 \(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;
}
posted @ 2024-03-08 22:13  Epp1adeR  阅读(18)  评论(0)    收藏  举报