这里会显示出您选择的修订版和当前版本之间的差别。
2020-2021:teams:legal_string:jxm2001:slope_trick [2021/08/26 21:45] jxm2001 创建 |
2020-2021:teams:legal_string:jxm2001:slope_trick [2021/08/27 17:34] (当前版本) jxm2001 |
||
---|---|---|---|
行 62: | 行 62: | ||
return 0; | return 0; | ||
} | } | ||
+ | </code> | ||
+ | </hidden> | ||
+ | ==== 例题二 ==== | ||
+ | |||
+ | [[https://www.luogu.com.cn/problem/CF1534G|CF1534G]] | ||
+ | |||
+ | === 题意 === | ||
+ | |||
+ | 给定一个人物,初始点为 $(0,0)$,人物每步可以从 $(x,y)$ 移动到 $(x+1,y)$ 或 $(x,y+1)$。 | ||
+ | |||
+ | 有 $n$ 个物品,坐标为 $(x_i,y_i)$,人物在坐标 $(x,y)$ 获得物品 $i$ 的代价为 $\max(|x-x_i|,|y-y_i|)$。 | ||
+ | |||
+ | 人物可以在任意位置选取任意数量的物品获取,问人物获取所有物品的最小代价。 | ||
+ | |||
+ | === 题解 === | ||
+ | |||
+ | 首先,假定人物的轨迹已经固定,对每个物品,考虑将物品按正方形区域扩大,直到与轨迹相交,显然在交点取该物品最佳。 | ||
+ | |||
+ | 不难发现,这等价于当人物坐标位于 $y=x_i+y_i-x$ 直线上时取第 $i$ 个物品,此时代价为 $|x-x_i|$。 | ||
+ | |||
+ | 设 $\text{dp}(k,x)$ 为人物位于坐标 $(x,k-x)$ 且已经获取所有 $x_i+y_i\le k$ 的物品的最小代价。 | ||
+ | |||
+ | 将所有物品按 $z_i=x_i+y_i$ 排序,于是有 | ||
+ | |||
+ | $$ | ||
+ | \text{dp}(z_i,x)=\min_{j\le z_i-z_{i-1}}\text{dp}(z_{i-1},j)+|x-x_i| | ||
+ | $$ | ||
+ | |||
+ | 发现状态转移分为两步,第一步是 $\text{dp}(z_i,x)\gets \min_{j\le z_i-z_{i-1}}\text{dp}(z_{i-1},j)$。 | ||
+ | |||
+ | 考虑将函数 $\text{dp}(k,x)$ 分成斜率小于 $0$,斜率等于 $0$ 和斜率大于 $0$ 三部分。 | ||
+ | |||
+ | 不难发现这步只需要将斜率大于 $0$ 部分整体向右偏移 $z_i-z_{i-1}$ 个单位,然后中间空缺部分用斜率等于 $0$ 的部分补全即可。 | ||
+ | |||
+ | 考虑分别用两个集合维护函数 $\text{dp}(k,x)$ 斜率小于 $0$ 部分和斜率大于 $0$ 部分的 $S$。 | ||
+ | |||
+ | 第二步是 $\text{dp}(z_i,x)\gets \text{dp}(z_i,x)+|x-x_i|$,也是分 $x_i$ 位于原函数斜率小于 $0$,斜率等于 $0$ 和斜率大于 $0$ 三部分讨论。 | ||
+ | |||
+ | 然后更新斜率小于 $0$ 部分和斜率大于 $0$ 部分的 $S$ 和斜率等于 $0$ 部分的答案即可。 | ||
+ | |||
+ | 例如当 $x_i$ 属于斜率小于 $0$ 的部分时,则原函数斜率等于 $0$ 的部分斜率变为 $+1$,但是斜率等于 $0$ 的部分的左端点仍是最优解。 | ||
+ | |||
+ | 于是左端点取值 $+|x-x_i|$ 就是新答案,然后将原来的左端点从斜率小于 $0$ 部分的 $S$ 移除,加入斜率大于 $0$ 部分的 $S$。 | ||
+ | |||
+ | 最后将两个 $x_i$ 插入斜率小于 $0$ 部分的 $S$ 就完成了更新。总时间复杂度 $O(n\log n)$。 | ||
+ | |||
+ | <hidden 查看代码> | ||
+ | <code cpp> | ||
+ | const int MAXN=8e5+5; | ||
+ | pair<int,int> a[MAXN]; | ||
+ | int main() | ||
+ | { | ||
+ | int n=read_int(); | ||
+ | _for(i,0,n){ | ||
+ | a[i].first=read_int(); | ||
+ | a[i].second=read_int(); | ||
+ | a[i].first+=a[i].second; | ||
+ | } | ||
+ | sort(a,a+n); | ||
+ | LL ans=0; | ||
+ | priority_queue<int> q1; | ||
+ | priority_queue<int,vector<int>,greater<int> > q2; | ||
+ | q1.push(a[0].second); | ||
+ | q2.push(a[0].second-a[0].first); | ||
+ | _for(i,1,n){ | ||
+ | if(a[i].second<q1.top()){ | ||
+ | int t=q1.top();q1.pop(); | ||
+ | ans+=t-a[i].second; | ||
+ | q1.push(a[i].second);q1.push(a[i].second); | ||
+ | q2.push(t-a[i].first); | ||
+ | } | ||
+ | else if(a[i].second<q2.top()+a[i].first){ | ||
+ | q1.push(a[i].second); | ||
+ | q2.push(a[i].second-a[i].first); | ||
+ | } | ||
+ | else{ | ||
+ | int t=q2.top()+a[i].first;q2.pop(); | ||
+ | ans+=a[i].second-t; | ||
+ | q2.push(a[i].second-a[i].first);q2.push(a[i].second-a[i].first); | ||
+ | q1.push(t); | ||
+ | } | ||
+ | } | ||
+ | enter(ans); | ||
+ | return 0; | ||
+ | } | ||
</code> | </code> | ||
</hidden> | </hidden> |