AT_jag2017autumn_j Farm Village

我怎么这么能随到 slope trick 题。这个翻译有点太简单了,去原题看数据范围。注意 \(d_i\)\(i\)\(i+1\) 这条边的单位代价,\(p_i\)\(i\) 号点生产一单位粮食的代价。

有一个很显然的费用流做法,但可惜他过不了。

发现这样的一个链式结构,如果我们选择费用流这种做法会失去一条边只链接了前后两部分的这个性质,于是我们以 \(i\)\(i+1\) 之间的这条边通过的流量为基准 dp。

\(f_{i,j}\)\(i\) 后面这条边经过了 \(j\) 单位粮食的最小代价,那么答案就是 \(f_{n-1,0}\)

考虑 dp,这个东西的式子写出来应该是这样的:

\(f_{i.j}=\min\limits_{k\in\{j-1,j,j+1\}}f_{i-1,k}+(k-j+1)p_i+|j|d_i\)

注意到 \(j\) 可能是负的,意义是流量既可以向左也可以向右。

易于发现这个式子是凸的,\(f_0\) 明显是凸的。抛去最后的绝对值函数,这个东西相当于给原来的式子加上一个一次函数后每个位置去相邻项的最小值,这个东西也是凸的。

于是就可以 slope trick 维护,在这个问题中,函数的正负分界点可能移动,所以要用两个堆来维护斜率分别为正负的部分。剩下的就是基本操作。

#include<iostream>
#include<queue>
using ll = long long;
ll n, d[200005], p[200005], rs, a; std::priority_queue<ll> q1;
std::priority_queue<ll, std::vector<ll>, std::greater<ll>> q2;
int main(){
	std::ios::sync_with_stdio(false);
	std::cin.tie(0),std::cout.tie(0);
  	std::cin >> n;
	for(int i = 1; i < n; i++) std::cin >> d[i];
	for(int i = 1; i <= n; i++) std::cin >> p[i];
	q1.push(p[1]), q2.push(p[1]);
	for(int i = 1; i < n; i++){
		rs += i * d[i]; a += d[i];
		if(p[i+1] < q2.top() + a)
			q1.push(p[i+1]+a), q1.push(p[i+1]+a),
			q2.push(q1.top() - 2 * a), q1.pop();
	 	else
			q2.push(p[i+1]-a), q2.push(p[i+1]-a),
			q1.push(q2.top() + 2 * a), q2.pop();
	}
	while(q1.size()) rs += q1.top() - a, q1.pop();
	std::cout << rs << '\n';
}
posted @ 2024-07-24 21:26  xlpg0713  阅读(4)  评论(0编辑  收藏  举报