Problem Description
在一个二维平面上有 nnn 个不同的点和一条 x+y=kx+y=kx+y=k 的直线,对于每个点 (xi,yi)(x_i,y_i)(xi,yi) 都满足 0≤xi,yi,xi+yi<k0 \le x_i,y_i,x_i+y_i < k0≤xi,yi,xi+yi<k 。
TenzingTenzingTenzing 想要删除所有点,他可以进行以下两种操作:
- 画三角形:选择两个非负整数 a,ba,ba,b ,满足 a+b<ka+b<ka+b<k ,则以 x=a,y=b和x+y=kx=a,y=b和x+y=kx=a,y=b和x+y=k 构成的三角形内的点都会被删除,设三角形边长为 l,l,2ll,l,\sqrt{2}ll,l,2l ,那么将花费 l×Al \times Al×A 。
- 删除一个特定的点 iii ,代价是 cic_ici 。
算出最小花费。
Input
第一行输入三个整数 n,k,A(1≤n,k≤2×105,1≤A≤104)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=kx+y=kx+y=k 相关系数和画三角形的基础花费的代价。
接下来 nnn 行每行输入三个整数 xi,yi,ci(0≤xi,yi,xi+yi<k,1≤ci≤104)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
对于两个重叠的三角形,我们会发现左图花费更大覆盖面积更小,右图花费更小覆盖面积更大,那么有个很显然的结论是三角形删点操作是相互分离、独立的,由此我们便考虑如何去 dpdpdp 。
我们设 dpidp_idpi 表示为删除 y≤iy \le iy≤i 的所有点的最小花费。
那么我们就可以得到一个比较暴力的状态转移:
dpi=minj∈[0,i−1](dpi−1+∑posy=icpos , dpj+A(i−j)+∑pos∈V2cpos)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\minmin 的左部中的 ∑posy=icpos\sum\limits_{pos_y=i}c_{pos}posy=i∑cpos ,只需在遍历到 iii 时对 posy=ipos_y=iposy=i 这一层的点权简单地求一遍和就行,整体复杂度是只有 O(n)O(n)O(n) 。
而对于右侧,这个是一个区间求最小值问题,那么此时我们便可以考虑是否可以改动公式来支持快速区间询问,以下给出一种改动方案:
dpj+A(i−j)+∑pos∈V2cpos→(dpj−A⋅j+∑pos∈V2cpos)+A⋅idp_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·idpj+A(i−j)+pos∈V2∑cpos→(dpj−A⋅j+pos∈V2∑cpos)+A⋅i
我们先只考虑 dpj−A⋅jdp_j-A·jdpj−A⋅j ,其实我们在每次求完 dpjdp_jdpj 时便能在线段树的 jjj 位置加上 dpj−A⋅jdp_j-A·jdpj−A⋅j ,那么在遍历到 iii 时,只需 querymin(0,i−1)query_{min}(0,i-1)querymin(0,i−1) 即可,那么现在的问题就出现在了如何处理 ∑pos∈V2cpos\sum\limits_{pos \in V_2}c_{pos}pos∈V2∑cpos 。
如上图所示,当我们的 jjj 在 L1L_1L1 的范围内取时,点 ppp 都是属于 ∑pos∈V2cpos\sum\limits_{pos \in V_2}c_{pos}pos∈V2∑cpos 的,那么此时我们就能很容易地想到,我们只需当遍历到 i==ypi==y_pi==yp 时在线段树上的 (xp,i](x_p,i](xp,i] 都加上 cpc_pcp ,而此处有两点值得注意:
- (xp,i](x_p,i](xp,i] 左端取开区间是由于当取到 j==xpj==x_pj==xp 时,点 ppp 此时是所做 V3V_3V3 的一部分,所以不应当加上 cpc_pcp 。
- 当我们算完第 iii 次 dpidp_idpi 时,应当在线段树上的 iii 位置加上 dpi−A⋅i−∑posy=icposdp_i-A·i-\sum\limits_{pos_y=i}c_{pos}dpi−A⋅i−posy=i∑cpos ,这是由于 dpidp_idpi 表示的是删除 y≤iy \le iy≤i 的所有点的最小花费,∑posy=icpos\sum\limits_{pos_y=i}c_{pos}posy=i∑cpos 已经包含在 dpidp_idpi 中,但是在线段树中又在 iii 位置上了,所以此处要减去以免重复计算。
最终时间复杂度为 O((n+k)logk)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;
}