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 0≤xi,yi,xi+yi<k 。
T e n z i n g Tenzing Tenzing 想要删除所有点,他可以进行以下两种操作:
- 画三角形:选择两个非负整数 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=b和x+y=k 构成的三角形内的点都会被删除,设三角形边长为 l , l , 2 l l,l,\sqrt{2}l l,l,2l ,那么将花费 l × A l \times A l×A 。
- 删除一个特定的点 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(1≤n,k≤2×105,1≤A≤104) ,表示点的个数,斜线 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(0≤xi,yi,xi+yi<k,1≤ci≤104) ,表示坐标和单个点删除的代价。
Output
输出删去所有点的最小花费。
Solution
对于两个重叠的三角形,我们会发现左图花费更大覆盖面积更小,右图花费更小覆盖面积更大,那么有个很显然的结论是三角形删点操作是相互分离、独立的,由此我们便考虑如何去 d p dp dp 。
我们设 d p i dp_i dpi 表示为删除 y ≤ i y \le i y≤i 的所有点的最小花费。
那么我们就可以得到一个比较暴力的状态转移:
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,i−1]min(dpi−1+posy=i∑cpos,dpj+A(i−j)+pos∈V2∑cpos)
对于
min
\min
min 的左部中的
∑
p
o
s
y
=
i
c
p
o
s
\sum\limits_{pos_y=i}c_{pos}
posy=i∑cpos ,只需在遍历到
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(i−j)+pos∈V2∑cpos→(dpj−A⋅j+pos∈V2∑cpos)+A⋅i
我们先只考虑
d
p
j
−
A
⋅
j
dp_j-A·j
dpj−A⋅j ,其实我们在每次求完
d
p
j
dp_j
dpj 时便能在线段树的
j
j
j 位置加上
d
p
j
−
A
⋅
j
dp_j-A·j
dpj−A⋅j ,那么在遍历到
i
i
i 时,只需
q
u
e
r
y
m
i
n
(
0
,
i
−
1
)
query_{min}(0,i-1)
querymin(0,i−1) 即可,那么现在的问题就出现在了如何处理
∑
p
o
s
∈
V
2
c
p
o
s
\sum\limits_{pos \in V_2}c_{pos}
pos∈V2∑cpos 。
如上图所示,当我们的 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} pos∈V2∑cpos 的,那么此时我们就能很容易地想到,我们只需当遍历到 i = = y p i==y_p i==yp 时在线段树上的 ( x p , i ] (x_p,i] (xp,i] 都加上 c p c_p cp ,而此处有两点值得注意:
- ( 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 。
- 当我们算完第 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} dpi−A⋅i−posy=i∑cpos ,这是由于 d p i dp_i dpi 表示的是删除 y ≤ i y \le i y≤i 的所有点的最小花费, ∑ p o s y = i c p o s \sum\limits_{pos_y=i}c_{pos} posy=i∑cpos 已经包含在 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;
}