\(\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;
}
posted on 2020-02-25 09:53  Oxide  阅读(27)  评论(0编辑  收藏  举报