[ROI 2017] 反物质(Day 2) 题解

题目在这里

解题思路

读完题后发现似乎有点像背包,那么可以设 \(f_i\) 表示获得 \(i\) 克反物质时最坏情况下利润的最大值。

为什么是最大值?

首先因为是最坏情况,所以要尽可能的倒霉些,但是我们做实验的方案不同,最坏情况下的利润也就不同,因此只要我们按照最坏情况下利润最大的那个方案做实验,我们获得的利润就绝对不会低于最坏情况下最大的利润。

然后我们就能推出 \(\mathcal{O}(na^2)\) 的转移方程式:

\[f_{i}=\max\limits_{j=1}^n\{\min\limits_{k=i+l_j}^{i+r_j}\{f_k+10^9(k-i)-c_j\}\} \]

反着推并不会影响正确性,答案即为 \(f_0\)

正着的我没搞出来。

考虑优化,看看能不能省略一些枚举,\(i,j\) 的枚举难以避免,考虑通过一些方法避免 \(k\) 的枚举。

可以发现:

\[\min\limits_{k=i+l_j}^{i+r_j}\{f_k+10^9(k-i)-c_j\}=\min\limits_{k=i+l_j}^{i+r_j}\{f_k+10^9k-10^9i-c_j\}=\min\limits_{k=i+l_j}^{i+r_j}\{f_k+10^9k\}-10^9i-c_j \]

\(-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!

posted @ 2025-05-24 14:09  lyas145  阅读(27)  评论(1)    收藏  举报