[ROI 2017] 反物质(Day 2) 题解
题目在这里。
解题思路
读完题后发现似乎有点像背包,那么可以设 \(f_i\) 表示获得 \(i\) 克反物质时最坏情况下利润的最大值。
为什么是最大值?
首先因为是最坏情况,所以要尽可能的倒霉些,但是我们做实验的方案不同,最坏情况下的利润也就不同,因此只要我们按照最坏情况下利润最大的那个方案做实验,我们获得的利润就绝对不会低于最坏情况下最大的利润。
然后我们就能推出 \(\mathcal{O}(na^2)\) 的转移方程式:
反着推并不会影响正确性,答案即为 \(f_0\)。
正着的我没搞出来。
考虑优化,看看能不能省略一些枚举,\(i,j\) 的枚举难以避免,考虑通过一些方法避免 \(k\) 的枚举。
可以发现:
\(-10^9i-c_j\) 能放到外面是因为这东西与 \(k\) 的取值无关。
接下来就是解决如何快速求 \(\min\limits_{k=i+l_j}^{i+r_j}\{f_k+10^9k\}\) 这个问题了。
很多人会和我一样想到开 \(n\) 个单调队列,但是空间复杂度可能会被卡到 \(\mathcal{O}(na)\)。
ST 表?空间复杂度 \(\mathcal{O}(a\log a)\) 依然不行。
咋办呢?
官方题解中提到了一种离线 RMQ 做法。
令 \(w(x)=f_x+10^9x\)(为了方便,下文有时称为 \(w\) 值)。
我们从 \(m\) 开始倒序枚举每个询问区间 \([i+l_j,i+r_j]\) 的左端点 \(i+l_j\),并维护一个并查集和单调栈(设栈顶元素为 \(t\))。我们想把 \(i+l_j\) 加入单调栈,必须满足 \(w(i+l_j)>w(t)\),如果不满足条件,则弹出栈顶元素,一直到满足条件或栈为空时加入 \(i+l_j\)。假设在此过程中 \(t_0\) 是不满足条件的栈顶元素,那么将 \(i+l_j\) 和 \(t_0\) 所在的集合合并,这时 \(w(i+l_j)\) 一定是 \(w(t_0)\) 左边第一个小于或等于 \(w(t_0)\) 的。再维护一下每个集合中最小的 \(w\) 值,我们就能求得目前 \(i+r_j\) 左边最小的 \(w\) 值,因为我们只处理到了 \(i+l_j\) 这个位置,所以目前 \(i+r_j\) 左边最小的 \(w\) 值就相当于 \([i+l_j,i+r_j]\) 中最小的 \(w\) 值。
若并查集使用路径压缩和按秩合并,那么时间复杂度就是 \(\mathcal{O}(na\alpha(a))\) 的,空间复杂度也能接受,毕竟单调栈中最多只会有 \(a\) 个元素。
码儿
#include<bits/stdc++.h>
#define qwq return
using namespace std;
typedef long long ll;
const int N=105,M=2e6+5,V=1e9;
int n,m;
ll f[M],s[M];
int fa[M],siz[M];
int l[N],r[N],c[N];
stack<int> stk;
inline int read() {
int x=0,f=1;
char c=getchar();
while (!isdigit(c)) {f=(c=='-'?-1:1);c=getchar();}
while (isdigit(c)) {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
qwq x*f;
}
ll w(int k) {qwq f[k]+1ll*V*k;}
int find(int x) {qwq fa[x]==x?x:fa[x]=find(fa[x]);}
void merge(int x,int y) {
if ((x=find(x))^(y=find(y))) {
if (siz[x]>siz[y]) {swap(x,y);}
fa[x]=y;
siz[y]+=siz[x];
s[y]=min(s[y],s[x]);
}
}
int main() {
n=read();m=read();
for (int i=1;i<=n;i++) {
l[i]=read();r[i]=read();c[i]=read();
}
for (int i=0;i<=m;i++) {
fa[i]=i;siz[i]=1;
}
for (int k=m;~k;k--) {
s[k]=w(k);
while (!stk.empty() && w(k)<=w(stk.top())) {
merge(k,stk.top());
stk.pop();
}
stk.push(k);
for (int j=1;j<=n;j++) {
int i=k-l[j];
if (i<0 || m<i+r[j]) {continue;}
f[i]=max(f[i],s[find(i+r[j])]-1ll*V*i-c[j]);
}
}
printf("%lld\n",f[0]);
qwq 0;
}
Thanks for reading!

浙公网安备 33010602011771号