[BZOJ3636]教义问答手册

教义问答手册

题解

挺简单的一道二分。

首先看到题目首先应该很容易想到dp,毕竟用dp求这个最大值应该是很简单的。
但是由于询问太多,我们不可能对每个询问都做一次dp,考虑整体二分。
对于二分时,我们处理掉所有当前的过 m i d mid mid的询问。
可以通过分别求出左边的dp值与右边的dp值来计算。
由于涉及到合并的问题,我们的状态 d p i , j dp_{i,j} dpi,j分别要记录它到了哪个点与它在 m i d mid mid处连续选的点的数量,表示选到第 i i i个点, m i d mid mid处连续选了 j j j个,时的最大值。
合并的时候只需要从 0 0 0 L L L枚举两边的dp来合并即可。
dp转移也就是一个很常见方法, d p i , j = m a x ( d p i − 1 , j , d p i − L , j + ∑ j = i − L + 1 i a j ) dp_{i,j}=max(dp_{i-1,j},dp_{i-L,j}+\sum_{j=i-L+1}^{i}a_{j}) dpi,j=max(dpi1,j,dpiL,j+j=iL+1iaj)
后面的求和可以用前缀和预处理出来。
注意由于要求前面与后面两段,要做两次dp

总时间复杂度为 O ( ( n + q ) L l o g   n ) O\left((n+q)Llog\, n\right) O((n+q)Llogn),还是可以过的。

源码

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
#include<set>
using namespace std;
#define MAXN 100005
#define lowbit(x) (x&-x)
#define reg register
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<int,int> pii;
const int mo=1e9+7;
const int INF=0x3f3f3f3f;
const double PI=acos(-1.0);
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
int n,L,q,a[MAXN],b[MAXN],c[MAXN],d[MAXN],pre[MAXN],suf[MAXN];
int f[MAXN][55],g[MAXN][55],sum[MAXN],ans[MAXN];
struct ming{int l,r;}s[MAXN];
void sakura(int l,int r,int al,int ar){
	int mid=l+r>>1;if(al>ar)return ;
	if(r-l+1<L){for(int i=al;i<=ar;i++)ans[b[i]]=0;return ;}
	if(l==r){for(int i=al;i<=ar;i++)ans[b[i]]=max(a[l],0);return ;}
	for(int i=0;i<=L;i++)f[mid][i]=sum[mid]-sum[mid-i];
	for(int i=mid-1;i>=l;i--)
		for(int j=0;j<=L;j++)
			if(i+L-1>mid-j)f[i][j]=f[i+1][j];
			else f[i][j]=max(f[i+1][j],f[i+L][j]+pre[i]);
	for(int i=mid;i>=max(mid-L+1,l);i--)
		for(int j=mid-i+2;j<=L;j++)f[i][j]=-INF;
	for(int i=0;i<=L;i++)g[mid+1][i]=sum[mid+i]-sum[mid];
	for(int i=mid+2;i<=r;i++)
		for(int j=0;j<=L;j++)
			if(i-L+1<=mid+j)g[i][j]=g[i-1][j];
			else g[i][j]=max(g[i-1][j],g[i-L][j]+suf[i]);
	for(int i=mid+1;i<=min(mid+L,r);i++)
		for(int j=i-mid+1;j<=L;j++)g[i][j]=-INF;
	for(int i=al;i<=ar;i++)
		if(s[b[i]].l<=mid&&mid<s[b[i]].r){
			int tmp=max(0,f[s[b[i]].l][0]+g[s[b[i]].r][0]);	
			for(int k=0;k<=L;k++)
				tmp=max(f[s[b[i]].l][k]+g[s[b[i]].r][L-k],tmp);
			ans[b[i]]=max(tmp,f[s[b[i]].l][L]+g[s[b[i]].r][L]);
		}
	int j=0,k=0;
	for(int i=al;i<=ar;i++){
		if(s[b[i]].r<=mid)c[++j]=b[i];
		if(s[b[i]].l>mid)d[++k]=b[i];
	}
	for(int i=al;i<al+j;i++)b[i]=c[i-al+1],c[i-al+1]=0;
	for(int i=ar;i>ar-k;i--)b[i]=d[ar-i+1],d[ar-i+1]=0;
	for(int i=l;i<=r;i++)
		for(int j=0;j<=L;j++)
			f[i][j]=g[i][j]=0;
	sakura(l,mid,al,al+j-1);sakura(mid+1,r,ar-k+1,ar);
}
signed main(){
	read(n);read(L);for(int i=1;i<=n;i++)read(a[i]),sum[i]=sum[i-1]+a[i];
	for(int i=1;i<=n;i++)pre[i]=sum[i+L-1]-sum[i-1],suf[i]=sum[i]-sum[i-L];
	read(q);for(int i=1;i<=q;i++)read(s[i].l),read(s[i].r),b[i]=i;
	sakura(1,n,1,q);for(int i=1;i<=q;i++)printf("%d\n",ans[i]);
	return 0;
}

谢谢!!!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值