CF1941E 题解

纪念一下蒟蒻第一次不看题解独立 AC 单调队列优化 dp。

思路

这题其实和 P1725 琪露诺很类似,这题的 dpidp_i 可以从 dpid1dpi1dp_{i-d-1}\sim dp_{i-1} 转移过来,然后当然选最小的,所以 dpi=ai+1+minj=id1i1dpjdp_i=a_i+1+\min\limits_{j=i-d-1}^{i-1}dp_j,然后这个最小值可以用单调队列维护。为了省时,我用了不带 log\log 的手写单调队列。

最后因为要求连续的 kk 行,所以可以用前缀和维护。

代码

# include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair <int, int> pii;
const ll inf = 1e18;
int t, n, m, x, k, d;
ll dp[200005], s[200005], minx;
deque <int> q;
int main () {
	ios::sync_with_stdio (0);
	cin.tie (0);
	cout.tie (0);
	cin >> t;
	while (t --) {
		cin >> n >> m >> k >> d;
		++ d;
		for (int i = 1; i <= n; ++ i) {
			q.clear ();
			for (int j = 1; j <= m; ++ j) {
				cin >> x;
				++ x;
				while (! q.empty () && q.front () < j - d)
					q.pop_front (); //过期了,删掉
				dp[j] = q.empty () ? x : dp[q.front ()] + x;
				while (! q.empty () && dp[q.back ()] >= dp[j])
					q.pop_back (); //把 >= dp[j] 的全部单调队列了(删了)
				q.push_back (j); //在队尾插入
			}
			s[i] = s[i - 1] + dp[m]; //前缀和
		}
		minx = inf;
		for (int i = k; i <= n; ++ i)
			minx = min (minx, s[i] - s[i - k]);
		cout << minx << '\n';
	}
	return 0;
}
posted @ 2024-04-02 21:00  Vitamin_B  阅读(8)  评论(0)    收藏  举报  来源