problem
LOJ2772
solution
这道题求的是“保证最多”,这个“保证”真的屑啊!
因为我们无法确定实验生成的克数,所以我们应当计算的是最坏情况。
又因为我们可以选择做哪些实验,所以要的是所有实验组合最坏情况下的最大价值。
如果题目读错成直接求最多,那么就会写出线段树优化 dpdpdp,结果样例都过不了!!
将答案数学化长相表示出来,设 dpi:dp_i:dpi: 已经装了 iii 克反物质,最大保证还能获得多少价值。
转移有,dpi=max1≤j≤n{minlj≤k≤rj{dpi+k+1e9⋅k−cj}}dp_i=\max_{1\le j\le n}\Big\{\min_{l_j\le k\le r_j}\{dp_{i+k}+1e9·k-c_j\}\Big\}dpi=max1≤j≤n{minlj≤k≤rj{dpi+k+1e9⋅k−cj}}。答案即为 dp0dp_0dp0。
其实只要搞清楚这一点,列出这个转移,剩下的优化部分已经是非常简单的了。
k∈[lj,rj]→i+k∈[i+lj,i+rj]k\in[l_j,r_j]\rightarrow i+k\in[i+l_j,i+r_j]k∈[lj,rj]→i+k∈[i+lj,i+rj],这浓浓的滑动窗口味儿。
稍微改写一下,初始化 dpi=1e9⋅idp_i=1e9·idpi=1e9⋅i,dpi=max1≤j≤n{minlj≤k≤rj{dpi+k}−cj}dp_i=\max_{1\le j\le n}\Big\{\min_{l_j\le k\le r_j}\{dp_{i+k}\}-c_j\Big\}dpi=max1≤j≤n{minlj≤k≤rj{dpi+k}−cj}。
然后对每个实验都开一个维护递增的单调队列,用队首更新答案。
i−−i--i−−,弹出单调队列中不在 [i+lj,i+rj][i+l_j,i+r_j][i+lj,i+rj] 的元素,每次都要新加 i+lji+l_ji+lj。
这些都是套路,时间复杂度 O(na)O(na)O(na)。
code
#include <bits/stdc++.h>
using namespace std;
#define maxn 105
#define maxv 2000005
#define int long long
const int NB = 1e9;
int n, a;
int l[maxn], r[maxn], c[maxn];
int f[maxv << 1];
deque < pair < int, int > > q[maxn];signed main() {scanf( "%lld %lld", &n, &a );for( int i = 1;i <= n;i ++ ) scanf( "%lld %lld %lld", &l[i], &r[i], &c[i] );for( int i = a + 1;i <= (a << 1);i ++ ) f[i] = -1e18; //将爆出实验的设为极小值 就不会被误判为答案for( int i = a;~ i;i -- ) {f[i] = NB * i;for( int j = 1;j <= n;j ++ ) {//[i+l(j),i+r(j)] 才会对i产生贡献 且是这段里的最小值贡献//i+l(j) 在i+1容量中没有被计入 这里计入while( ! q[j].empty() and q[j].back().second >= f[i + l[j]] ) q[j].pop_back(); //维护单增队列q[j].push_back( make_pair( i + l[j], f[i + l[j]] ) );while( ! q[j].empty() and q[j].front().first > i + r[j] ) q[j].pop_front(); //弹出不在范围内的f[i] = max( f[i], q[j].front().second - c[j] ); //实验是可以选的 所以外层dp比较取较大值}}printf( "%lld\n", f[0] );return 0;
}