BZOJ3636 教义问答手册(分治+DP+细节处理)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/C20180602_csq/article/details/102331508

3636: 教义问答手册

Description

“汉中沃野如关中,四五百里烟蒙蒙。黄云连天夏麦熟,水稻漠漠吹秋风。”——摘自 黄裳《汉中行》
“泉岭精神不朽,汉中诸球永生。”——摘自《泉岭精神创立者语录》
“把神犇烤一烤,味道会更好。”——摘自《xhr语录》
“秀恩爱有利于身心健康!”——摘自《泉岭精神集大成者语录》
“楼上说的对!”——摘自《泉岭精神信徒语录合集》
“不会做积分,怎么找妹子!”——摘自《xhr语录》
“切实保护耕地以放置更多的哨戒炮。”——摘自《泉岭精神信徒语录合集》
“就算两个包子一起吃掉,也不能阻止我修筑梯田。”——摘自《泉岭精神创立者语录》
“我来自泉岭,他来自汉中,我们半道而逢。”——摘自《泉岭精神集大成者语录》
【问题描述】
    作为泉岭精神的缔造者、信奉者、捍卫者、传承者,Pear决定印制一些教义问答手册,以满足泉岭精神日益增多的信徒。Pear收集了一些有关的诗选、语录,其中部分内容摘录在了【题目背景】里。这些语录是按出现的时间排好序的——Pear很喜欢这样的作风,于是决定在按时间排好序的基础上,选择部分语录,制作成若干本教义问答手册。
    一共有N条语录。Pear决定从中选出某一段时间内的所有语录,在此基础上印制大小为L的若干本教义问答手册。Pear对印制的手册有如下要求:
    1.每本手册必须包含这个区间内连续的恰好L条语录。
    2.不同手册包含的语录不能相同。
    3.每条语录有一个“主题相关程度”,这个数可正可负。Pear希望所有手册的语录的“主题相关程度”之和尽可能大。
    例如,对于区间[3,15]和L=3,一种选择方法是:[4,6]+[9,11]+[12,14]。这三个区间长度都恰好为L,且互不重叠。
    Pear并没有决定选哪段时间的语录,因此他有Q次询问。每次询问,给出两个数[l,r]表示候选语录的范围是第l条到第r条。你能回答出每个询问的最大“主题相关程度”之和么?

Input

    第一行两个正整数N,L,含义如上所述。注意对于所有询问,L都是一样的。
    第二行N个整数,绝对值<=10000。第i个数表示第i条语录的“主题相关程度”。
    接下来Q行,每行两个正整数l和r,表示询问区间。

Output

    输出Q行,每行表示这组询问的答案。注意,这个答案可以是0,如果区间负数过于多的话。

Sample Input

15 3
3 1 5 -2 3 -2 -2 2 2 2 0 3 2 -1 0
9
8 10
10 10
9 11
2 14
5 14
5 13
12 13
7 13
2 10

 

Sample Output

6
0
4
17
11
11
0
11
12

HINT

【数据范围】

    对于10%的数据,N=1000,Q=1000,L<=50

    对于另外20%的数据,N=100000,Q=100000,L<=5

    对于另外20%的数据,N=100000,Q=100000,L<=10

    对于100%的数据,N=100000,Q=100000,L<=50


题意见:https://blog.csdn.net/C20181220_xiang_m_y/article/details/102251740

实际上题解也可以

注意L的大小是固定的!!!

题解

好难写的一道题。。。

其实它的思路很简单

我们先考虑如何回答一个询问

我们可以确定左端点,然后直接扫一遍这个区间来DP

我们对于10%的数据可以枚举每一个左端点,把每一个区间的答案预处理出来

设 f[i][j] 表示区间[i,j]的最大答案

则明显有转移:f[i][j]=max(f[i][j-1],f[i][j-L]+sum[j]-sum[j-L])                     (sum表示前缀和)

但是由于L是固定的,所以我们可以把sum[x]重新定义为a[x-L+1]+a[x-L+2]+......+a[x]

则转移可以化简为f[i][j]=max(f[i][j-1],f[i][j-L]+sum[j])

但是这并没有用,时间复杂度始终是O(n^2)

因为L比较小

于是我们可以想到另外一种计算单个询问的方法

我们考虑把一个询问从中间的某一个地方(假设是mid(但是这里的mid不一定是当前询问区间的mid(为分治做铺垫)))分开

那么这个询问的答案可以这样计算:

f[l][r]=max(f[l][x]+f[x+L+1][r]+(a[x+1]+a[x+2]+......+a[x+L])  ,  f[l][mid]+f[mid+1][r])

       =max(f[l][x]+f[x+L+1][r]+sum[x+L]  ,  f[l][mid]+f[mid+1][r])

其中前者是有被选子区间跨过mid点的答案,后者是没有被选子区间跨mid点的答案

mid-L+1<=x<=mid+L-1

由于我们可以O(n)算出有一个固定端点的所有子区间的答案

所以我们在总区间上选定一个点mid

将mid-L+1~mid的点一个个固定为右端点来预处理f[l][x]

将mid+1~mid+L-1的点一个个固定为左端点来预处理f[x][r]

(当然直接用x为下标会MLE,所以DP的时候要换一下下标(这也导致了后面的细节暴多))

虽然这样做会让一个询问的复杂度变成O(L+nL)

但是这样可以一次性把所有跨过mid点的区间算完,总共O(qL+nL)

于是我们可以考虑把所有询问一起分治

直接算出跨过mid点的区间的答案,然后没有跨过mid点的区间就变成子问题来解决

总时间复杂度O(QL+nLlogn)

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,L,m;
#define N 100005
struct node{
	int l,r,i;
}q[N],nq[N];
int a[N],sum[N],ans[N];
int fl[55][N],fr[55][N];
void prel(int l,int r)
{
	for(int i=1;i<=min(L,r-l+1);i++){
		fl[i][0]=0;
		for(int j=1;i+j-1<=r-l+1;j++){
			if(j>=L)
				fl[i][j]=max(fl[i][j-1],fl[i][j-L]+sum[r-i-j+2+L-1]);
			else
				fl[i][j]=0;
		}
	}
}
void prer(int l,int r)
{
	for(int i=1;i<=min(L,r-l+1);i++){
		fr[i][0]=0;
		for(int j=1;i+j-1<=r-l+1;j++){
			if(j>=L)
				fr[i][j]=max(fr[i][j-1],fr[i][j-L]+sum[l+i+j-2]);
			else
				fr[i][j]=0;
		}
	}
}
void solve(int l,int r,int ql,int qr)
{
	if(ql>qr||r-l+1<L) return;
	int mid=(l+r)>>1,cnt1=ql-1,cnt2=qr+1;
	prel(l,mid);prer(mid+1,r);
	for(int i=ql;i<=qr;i++){
		if(q[i].r<=mid) nq[++cnt1]=q[i];
		else if(q[i].l>mid) nq[--cnt2]=q[i];
		else{
			ans[q[i].i]=fl[1][mid-q[i].l+1]+fr[1][q[i].r-mid];
			for(int j=max(1,q[i].l+L-1-mid);j<min(L,q[i].r-mid+1);j++){//写得极其复杂
				ans[q[i].i]=max(ans[q[i].i],sum[mid+j]
				+fr[j+1][q[i].r-(mid+j+1)+1]
				+fl[L-j+1][(mid+j-L+1-1)-q[i].l+1]);
			}
		}
	}
	for(int i=ql;i<=cnt1;i++)q[i]=nq[i];
	for(int i=qr;i>=cnt2;i--)q[i]=nq[i];
	solve(l,mid,ql,cnt1);
	solve(mid+1,r,cnt2,qr);
}
int main()
{
	int i;
	scanf("%d%d",&n,&L);
	for(i=1;i<=n;i++){
		scanf("%d",&a[i]);
		sum[i]=sum[i-1]+a[i];
	}
	for(i=n;i>=L;i--)sum[i]-=sum[i-L];
	scanf("%d",&m);
	for(i=1;i<=m;i++){
		scanf("%d%d",&q[i].l,&q[i].r);
		q[i].i=i;
	}
	solve(1,n,1,m);
	for(i=1;i<=m;i++)
		printf("%d\n",ans[i]);
}

终于A了!!!!!!!!!!!!!!

猜你喜欢

转载自blog.csdn.net/C20180602_csq/article/details/102331508