题解 P4852 yyf hates choukapai
一道毒瘤的dp
状态难设计,转移较简单。
设计状态及方程
题意其实很好理解,抽卡,找运气最大值,dp应该稳的。
然后看题,关键信息有几个:
-
不能连续单抽
d 次 ,这意味着我们的状态必须记录或推导到开始单抽的时刻。 -
有连抽和单抽的次数限制,必须记录连抽或单抽的次数。
但是这边我们有了很多种选择,蒟蒻本人连续想假了三个状态QAQ。
最终确定
即,
不过这个状态好像不如标程好转移,不过这是蒟蒻能想通并写出方程的较好理解的状态了。
然后考虑到恰好用了
找方案应该是没啥问题的,就是记下每个状态是从哪里转移的,然后打个指针过去,然后从后面递推回去输出即可。
代码
#include<bits/stdc++.h>
#define rep(a,b,c) for(register int c=(a);c<=(b);++c)
using namespace std;
const int N=2e5+5,INF=-0x3f3f3f3f,M=45;
inline int read()
{
int res=0;char ch=getchar();while(ch<'0'||ch>'9')ch=getchar();
while(ch<='9'&&ch>='0')res=res*10+(ch^48),ch=getchar();return res;
}
int dp[N][M],sum[N],nxt[N][M],c,d;
struct DEQ{int val,num;}deq[N];inline void To(DEQ &k,int x,int y){k.val=x;k.num=y;}int h,t;
inline void print(int cur,int i){if(!i)return; print(nxt[cur][i],i-1);printf("%d ",cur-c+1);}
int main()
{
int n=read(),m=read();c=read(),d=read();
int len=n*c+m;rep(1,len,i)sum[i]=sum[i-1]+read();
memset(dp,-0x3f,sizeof(dp));rep(1,d,i)dp[i][0]=sum[i];dp[0][0]=0;
rep(1,n,j){h=1;t=0;rep(c,len,i)if(i-c*j<=m)
{
while(h<=t&&deq[h].num<((i>c+d&&c+1>i-c-d)?c+1:i-c-d))h++;
while(h<=t&&deq[t].val<=(dp[i-c][j-1]-sum[i-c]))t--;To(deq[++t],dp[i-c][j-1]-sum[i-c],i-c);
dp[i][j]=deq[h].val+sum[i-c+1];nxt[i][j]=deq[h].num;
}}
int ans=0,mx=0;rep(len-d,len,i)if(dp[i][n]+sum[len]-sum[i]>ans)mx=i,ans=dp[i][n]+sum[len]-sum[i];
printf("%d\n",ans);print(mx,n);return 0;
}
码风较丑仅供对拍(滑稽)。