传送门:CF
[前题提要]:感觉trick应该挺典的,但是这种trick题就是这样的,当你想到的时候就秒,不知道就想不出来.虽然感觉很典,但是和平时CF上的trick不太一样,故记录一下.
当我第一眼看到这道题的时候,其实我马上就想到了一个dp方程,可以使用 d p [ i ] [ j ] [ k ] dp[i][j][k] dp[i][j][k]来表示前 i i i个物品在容量为 j j j的背包下,第 i i i个物品选择 k k k个的方案数.这个dp方程是不难转移的,使用一下前缀最大值和后缀最大值优化一下就行.但是复杂度是 n ∗ m ∗ m n*m*m n∗m∗m的,显然过不去,但是你会发现你选择的物品最多只能是k左右两边500个,所以此时复杂度可以优化为 1000 ∗ m 2 1000*m^2 1000∗m2,仍然是 2 e 8 2e8 2e8,加上一些常数,不幸的是还是过不去.其实我个人感觉是可以放这个方法过的,时限开个4s差不多就行了,但是出题人可能就是为了让你用他的trick,故意卡的.那没办法,就当被教育了.
ok,现在开始讲一下这道题的trick,对于本题的限制,我们会发现假设我们想选一个 i i i此时的 i i i小于 k k k,那么我们必然要将区间 [ i , k ] [i,k] [i,k]中的所有数字都选上一遍.如果我们想选一个 i i i此时的 i i i大于 k k k,那么我们必然要将区间 [ k , i ] [k,i] [k,i]的所有数字都选上一遍.那么此时,我们可以将上述的性质总结一下,我们会发现,其实我们就是每次都必须要选一个包含 k k k的子区间.考虑为什么选一个包含 k k k的子区间是必然满足限制的.因为我们是在 k k k左边区间一个 l l l, k k k右边取一个 r r r,然后我们会发现遵循上述的选法,对于一个 i i i小于 k k k,我们多选了一个这个 i i i,必然伴随着 [ i + 1 , k ] [i+1,k] [i+1,k]的所有数字都被选了一遍. i i i大于 k k k同理,所以此时的正确性是对的.
所以我们可以考虑将所有包含k的子区间合并成一个新的物品.然后我们在这些物品中跑一个完全背包即可.
因为大多数的物品合成后的体积都会变得很大,所以实际复杂度很小.
下面是具体的代码部分:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define root 1,n,1
#define ls (rt<<1)
#define rs (rt<<1|1)
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
inline ll read() {
ll x=0,w=1;char ch=getchar();
for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
return x*w;
}
inline void print(__int128 x){
if(x<0) {putchar('-');x=-x;}
if(x>9) print(x/10);
putchar(x%10+'0');
}
#define maxn 1000000
const double eps=1e-8;
#define int_INF 0x3f3f3f3f
#define ll_INF 0x3f3f3f3f3f3f3f3f
struct Node{
int w,v;
}node1[maxn],node2[maxn];int cnt=0;
int sumw[maxn],sumv[maxn];
int dp[maxn];
int main() {
int n=read();int m=read();int k=read();
for(int i=1;i<=n;i++) {
node1[i].w=read();node1[i].v=read();
}
for(int i=1;i<=n;i++) {
sumw[i]=sumw[i-1]+node1[i].w;
sumv[i]=sumv[i-1]+node1[i].v;
}
for(int i=1;i<=k;i++) {
for(int j=k;j<=n;j++) {
node2[++cnt].w=sumw[j]-sumw[i-1];
node2[cnt].v=sumv[j]-sumv[i-1];
}
}
for(int i=1;i<=cnt;i++) {
for(int j=node2[i].w;j<=m;j++) {
dp[j]=max(dp[j],dp[j-node2[i].w]+node2[i].v);
}
}
for(int i=1;i<=m;i++) {
cout<<dp[i]<<" ";
}
cout<<endl;
return 0;
}