\(\sf Eden\) 的新背包问题
解法
因为每次只会删掉一个,而且不会保留。先分别预处理出从前往后,从后往前的多重背包。询问时假设去掉玩偶 \(x\),枚举前 \(x-1\) 个玩偶的花费 \(w\),就可以这样转移
\[\text{Ans}=\max_{w=0}^m\{pre_{x-1,w}+suf_{x+1,m-w}\}
\]
复杂度是 \(\mathcal O(nmc+qm)\) 的。
不过事实上可以用 \(\rm cdq\) 分治来优化这个过程。假设分治到区间 \([l,r]\) 时得到的 \(\mathtt{dp}\) 值是删去 \([l,r]\) 得到的最大值。那么显然 \([l,\text{mid}]\) 对 \((\text{mid},r]\) 有贡献;\((\text{mid},r]\) 对 \([l,\text{mid}]\) 有贡献。所以我们可以先计算出 \([l,\text{mid}]\) 的 \(\mathtt{dp}\) 值再进入 \((\text{mid},r]\),另一半同理。
具体 \(\mathtt{dp}\) 时还可以进行单调队列优化。假设加入物品 \((c_i,val_i)\)。枚举容量 \(j\) 取模 \(c_i\) 的余数 \(r\),对每种余数分别 \(\mathtt{dp}\):
\[dp_{i,j}=\max\{dp_{i-1,k\cdot c_i+r}-k\cdot val_i\}+\frac{j}{c_i}\cdot val_i
\]
实现的时候要记得是将 \(i-1\) 状态的 \(\mathtt{dp}\) 值放入队列。
复杂度是 \(\mathcal O(nV\cdot \log n)\)。
代码
#include <cstdio>
#define print(x,y) write(x),putchar(y)
template <class T>
inline T read(const T sample) {
T x=0; char s; bool f=0;
while((s=getchar())>'9' or s<'0')
f|=(s=='-');
while(s>='0' and s<='9')
x=(x<<1)+(x<<3)+(s^48),
s=getchar();
return f?-x:x;
}
template <class T>
inline void write(const T x) {
if(x<0) {
putchar('-'),write(-x);
return;
}
if(x>9) write(x/10);
putchar(x%10^48);
}
#include <queue>
#include <vector>
#include <cstring>
#include <iostream>
using namespace std;
const int maxn=1005,maxq=3e5+5;
int n,a[maxn],b[maxn],c[maxn];
int ans[maxq],ban[maxq],V[maxq];
int dp[12][maxn];
vector <int> g[maxn];
deque < pair<int,int> > q;
void getDp(int d,int x) {
for(int r=0;r<a[x];++r) {
while(!q.empty())
q.pop_back();
q.push_back(make_pair(0,dp[d][r]));
for(int i=1;i*a[x]+r<=1000;++i) {
while(!q.empty() and
q.back().second<=dp[d][i*a[x]+r]-i*b[x])
q.pop_back();
q.push_back(make_pair(i,dp[d][i*a[x]+r]-i*b[x]));
while(!q.empty() and q.front().first<i-c[x])
q.pop_front();
dp[d][i*a[x]+r]=max(dp[d][i*a[x]+r],q.front().second+i*b[x]);
}
}
}
void dicon(int d,int l,int r) {
if(l==r) {
for(int i=0;i<g[l].size();++i)
ans[g[l][i]]=dp[d-1][V[g[l][i]]];
return;
}
int mid=l+r>>1;
memcpy(dp[d],dp[d-1],sizeof dp[d]);
for(int i=mid+1;i<=r;++i)
getDp(d,i);
dicon(d+1,l,mid);
memcpy(dp[d],dp[d-1],sizeof dp[d]);
for(int i=l;i<=mid;++i)
getDp(d,i);
dicon(d+1,mid+1,r);
}
int main() {
n=read(9); int q;
for(int i=1;i<=n;++i)
a[i]=read(9),b[i]=read(9),c[i]=read(9);
q=read(9);
for(int i=1;i<=q;++i) {
ban[i]=read(9)+1,V[i]=read(9);
g[ban[i]].push_back(i);
}
dicon(1,1,n);
for(int i=1;i<=q;++i)
print(ans[i],'\n');
return 0;
}