P4852 yyf hates choukapai

考虑(dp),设状态量(dp[i][j])表示抽到(i)用了(j)次连抽的最大值。

预处理前缀和:(sum[i]=sumlimits_{j=1}^i a[j])

状转方程:(dp[i][j]=max{quad dp[i-k-c][j-1]+a[i-k-c+1]+sum[i]-sum[i-k]quad |quad kin[0,d]quad})

意思是可以选长度为(k)的一段单抽,然后来一次连抽。

注意到没有(i)(i-k)的关联项,故直接单调队列维护即可。

时间复杂度(Theta(n*(cn+m)))

代码如下,仅供参考:

#include<bits/stdc++.h>
using namespace std;
inline int read(){
	int x=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return x*f;
}
int n,m,c,d,tot;
const int maxn=2e5+10;
int a[maxn],sum[maxn],dp[maxn][50];
int p[maxn][50],q[maxn],l,r;
inline void out(int i,int j){
	if(j==0)return;
	out(p[i][j]-1,j-1);
	printf("%d ",p[i][j]);
}
int main(){
	//freopen("P4852.in","r",stdin);
	//freopen("P4852.out","w",stdout);
	n=read(),m=read(),c=read(),d=read();
	tot=c*n+m;
	for(int i=1;i<=tot;i++){
		a[i]=read();
		sum[i]=sum[i-1]+a[i];
	}
	for(int i=1;i<=d;i++)
		dp[i][0]=sum[i];
	for(int j=1;j<=n;j++){
		memset(q,0,sizeof(q));
		l=1,r=0;
		for(int i=j*c;i<=tot;i++){
			dp[i][j]=dp[i-c][j-1]+a[i-c+1];
			p[i][j]=i-c+1;
			while(l<=r&&i-q[l]>d)l++;
			if(l<=r&&dp[i][j]<dp[q[l]-c][j-1]+a[q[l]-c+1]+sum[i]-sum[q[l]]){
				dp[i][j]=dp[q[l]-c][j-1]+a[q[l]-c+1]+sum[i]-sum[q[l]];
				p[i][j]=q[l]-c+1;
			}
			while(l<=r&&dp[i-c][j-1]-sum[i]+a[i-c+1]>=dp[q[r]-c][j-1]+a[q[r]-c+1]-sum[q[r]])r--;
			q[++r]=i;
		}
	}
	printf("%d
",dp[tot][n]);
	out(tot,n);
	return 0;
}

深深地感到自己的弱小。

原文地址:https://www.cnblogs.com/syzf2222/p/13842339.html