Codeforces Round 922 (Div. 2) D题 Blocking Elements(单调队列优化dp)
题目链接
https://codeforces.com/problemset/problem/1918/D
思路
我们很容易想到二分答案。
假设我们已经确定答案不会超过 x x x,那么有:两个屏蔽点间的元素和不大于 x x x,所有屏蔽点的权值和不大于 x x x。
我们在原序列的基础上加上两个虚拟点 a 0 = a n + 1 = 0 a_{0} = a_{n+1} = 0 a0=an+1=0,我们设 d p i dp_{i} dpi表示考虑前 i i i个位置,且两个屏蔽点间的元素和的最大值不超过 x x x的最小代价,状态转移方程为:
d p [ i ] = a [ i ] + m i n ( d p [ j ] ) dp[i] = a[i] + min(dp[j]) dp[i]=a[i]+min(dp[j]),其中 j < i j < i j<i 并且 s [ i − 1 ] − s [ j ] ≤ x s[i-1]-s[j] \le x s[i−1]−s[j]≤x, s s s为 a a a的前缀和数组。
因为每次转移到 i i i的区间,无论是前缀和数组 s s s还是 d p dp dp数组都单调递增,因此我们可以使用单调队列优化 d p dp dp,将时间复杂度降至线性。
最后 d p [ n + 1 ] dp[n+1] dp[n+1]即为最终答案。
代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e5 + 5;
int n;
int a[N], s[N], dp[N];
deque<int>q;
bool check(int x)
{q.clear();for (int i = 1; i <= n + 1; i++)dp[i] = 0;q.push_back(0);for (int i = 1; i <= n + 1; i++){while (q.size() && s[i - 1] - s[q.front()] > x) q.pop_front();dp[i] = a[i] + dp[q.front()];while (q.size() && dp[q.back()] >= dp[i]) q.pop_back();q.push_back(i);}return dp[n + 1] <= x;
}
void solve()
{cin >> n;for (int i = 1; i <= n; i++){cin >> a[i];}a[n + 1] = 0;for (int i = 1; i <= n + 1; i++)s[i] = s[i - 1] + a[i];int low = 0, high = 1e14;while (low < high){int mid = low + high >> 1;if (check(mid)){high = mid;}else low = mid + 1;}cout << high << endl;
}
signed main()
{ios::sync_with_stdio(false);cin.tie(0), cout.tie(0);int test = 1;cin >> test;for (int i = 1; i <= test; i++){solve();}return 0;
}