题目:
题意:
每张卡片都有一定的幸运值,现在一共有 n ∗ c + m n*c+m n∗c+m张牌,要求必须连抽 c c c次,每次 n n n张牌,而单抽不能连续 d d d次,问最大的幸运值是多少
分析:
对于连抽,我们可以理解成:在第
i
i
i位开始抽,对答案的贡献只有
a
i
a_i
ai。换而言之,当我们选
i
i
i作为连抽起点时,我们失去的幸运值为
∑
j
=
i
+
1
i
+
c
−
1
a
j
\sum_{j=i+1}^{i+c-1}a_j
∑j=i+1i+c−1aj。所以我们可以设
b
i
b_i
bi表示以
i
i
i为起点时我们失去的幸运值
这样一来,题目就转化为在
n
∗
c
+
m
n*c+m
n∗c+m个位置中选择
n
n
n个,使得
b
b
b的总和最小
我们设
f
i
,
j
f_{i,j}
fi,j表示在
1
−
i
1-i
1−i的位置里,选择了
j
j
j个起点且
i
i
i也一定选择了
所以
f
i
,
j
=
f
k
,
j
−
1
+
b
i
(
k
∈
(
i
−
c
−
d
,
i
−
c
)
)
f_{i,j}=f_{k,j-1}+b_i(k\in(i-c-d,i-c))
fi,j=fk,j−1+bi(k∈(i−c−d,i−c))
但因为这样的时间复杂度实在承受不起,于是我们考虑下如何优化
d
p
dp
dp
然后就有了单调队列优化,因为起点越靠后而失去的幸运值是一样的,那么显然是靠后的更优
代码:
// luogu-judger-enable-o2
#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#define LL long long
#define LZX ILU
using namespace std;
inline LL read() {
LL d=0,f=1;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9'){d=d*10+s-'0';s=getchar();}
return d*f;
}
int x[200005],cnt[200005],p[200005],f[200005][45];
struct mo{
int w,i;
}q[200005][45];
int fa[200005][45],h[45],t[45];
void work(int k,int king)
{
if(!k) return;
work(fa[k][king],king-1);
printf("%d ",k);
return;
}
int main()
{
int n=read(),m=read(),c=read(),d=read();
int s=c*n+m;
for(int i=1;i<=s;i++) x[i]=read(),cnt[i]=cnt[i-1]+x[i];
for(int i=1;i<=s-c+1;i++) p[i]=cnt[i+c-1]-cnt[i];
int k,ans=0;
for(int i=1;i<=n;i++) h[i]=1;
for(int i=1;i<=s-c+1;i++)
{
if(i<=c) {f[i][1]=p[i];continue;}
for(int j=(i+c-2)/(c+d)+1;j<=n&&j<=(i+c-1)/c;j++)
{
if(h[j-1]<=t[j-1]&&q[h[j-1]][j-1].i<i-c-d) h[j-1]++;
if(j-1>=(i-2)/(c+d)+1&&j-1<=(i-1)/c)
{
while(h[j-1]<=t[j-1]&&q[t[j-1]][j-1].w>=f[i-c][j-1]) t[j-1]--;
q[++t[j-1]][j-1].w=f[i-c][j-1];
q[t[j-1]][j-1].i=i-c;
}
f[i][j]=q[h[j-1]][j-1].w+p[i];
fa[i][j]=q[h[j-1]][j-1].i;
if(i>=s-c-d+1&&j==n&&ans<cnt[s]-f[i][j]) ans=cnt[s]-f[i][j],k=i;
}
}
printf("%d\n",ans);
work(k,n);
return 0;
}