题目

传送门

解法

无论多么麻烦的代码写完过后都要耐着性子检查

首先有朴素 \(n^2\)\(\mathtt{dp}\)

感觉这个思路太神辣!对区间 \([1,n]\) 进行分治。考虑在询问 \(i\) 第一次 覆盖区间 \(\text{mid}\) 时计算这个询问。为什么这是正确的?其实也就是为什么 \([l,r]\) 一定覆盖这个询问?容易想到如果 \([l,r]\) 未覆盖询问 \(i\),询问 \(i\) 一定越过了某个端点,而且这个端点不是 \(1/n\),也就是说一定是之前二分的一个 \(\text{mid}\),这和第一次覆盖区间 \(\rm mid\) 的假设相悖,所以得证。

不过如果我们还用朴素 \(\mathtt{dp}\),一切都是白瞎。甚至成功升高了复杂度。

发现朴素 \(\mathtt{dp}\) 复杂度那么高是因为要保证选取的数大于等于 \(l\),小于等于 \(r\) 这两个条件,如果我们把这两个条件拆开做,再合并起来会怎样呢?看上去十分不可做

在之前的操作中,我们保证了一个很好的性质:\(l_i\le \text{mid},r_i>\text{mid}\)。那么所有询问都可以被 \(\rm mid\) 划分成两段,如果分别从 \(\text{mid},\text{mid}+1\) 开始向两边 \(\mathtt{dp}\),我们就确定了一个端点,单次 \(\mathtt{dp}\) 复杂度就是 \(\mathcal O(n)\) 的啦!具体而言,枚举在 \([L,\text{mid}]\) 之间有多少条被选的语录,所以一次 \(\mathtt{dp}\) 实际是 \(\mathcal O(nL)\) 的,然后花费 \(\mathcal O(L)\) 将其拼在一起。

时间复杂度 \(\mathcal O(nL\log n)\)

代码

#include <cstdio>
#define rep(i,_l,_r) for(signed i=(_l),_end=(_r);i<=_end;++i)
#define fep(i,_l,_r) for(signed i=(_l),_end=(_r);i>=_end;--i)
#define print(x,y) write(x),putchar(y)

int read() {
	int x=0,f=1; char s;
	while((s=getchar())>'9' || s<'0') if(s=='-') f=-1;
	while(s>='0' && s<='9') x=(x<<1)+(x<<3)+(s^48),s=getchar();
	return x*f;
}

void write(int x) {
	if(x<0) return (void)(putchar('-'),write(-x));
	if(x>9) write(x/10);
	putchar(x%10^48);
}

#include <iostream>
using namespace std;

const int maxn=1e5+5;

int n,L,a[maxn],m,f[55][maxn],g[55][maxn],ans[maxn];
struct node {int l,r,id;} q[maxn],tl[maxn],tr[maxn];

void calcL(int l,int r) {
	rep(i,0,min(r-l,L)) { // 枚举中间那 L 条语录在 [l,mid] 中有多少条
		f[i][r-i+1]=0;
		fep(j,r-i,l)
			f[i][j]=max(f[i][j+1],(j+L-1>r-i?0:f[i][j+L]+a[j+L-1]));
	}
}

void calcR(int l,int r) {
	rep(i,0,min(r-l,L)) {
		g[i][l+i-1]=0;
		rep(j,l+i,r)
			g[i][j]=max(g[i][j-1],(j-L+1<l+i?0:g[i][j-L]+a[j]));
	}
}

void dicon(int l,int r,int ql,int qr) {
	if(ql>qr || r-l+1<L) return;
	int mid=l+r>>1,totl=0,totr=0;
	calcL(l,mid),calcR(mid+1,r);
	rep(i,ql,qr) 
		if(q[i].l>mid) tr[++totr]=q[i];
		else if(q[i].r<=mid) tl[++totl]=q[i];
		else {
            if(q[i].l<l || q[i].r>r) puts("fuck bitch!");
			ans[q[i].id]=max(f[0][q[i].l]+g[0][q[i].r],0);
			rep(j,max(1,L+mid-q[i].r),min(mid-q[i].l+1,L)) 
				ans[q[i].id]=max(ans[q[i].id],f[j][q[i].l]+g[L-j][q[i].r]+a[mid+L-j]);
			// mid-j+1>=l -> j<=mid-l+1
			// L-j+mid<=r -> j>=L+mid-r
		}
	rep(i,1,totl) q[ql+i-1]=tl[i];
	rep(i,1,totr) q[ql+totl+i-1]=tr[i];
	dicon(l,mid,ql,ql+totl-1),dicon(mid+1,r,ql+totl,ql+totl+totr-1);
}

int main() {
	n=read(),L=read();
	rep(i,1,n) a[i]=a[i-1]+read();
	fep(i,n,L) a[i]-=a[i-L];
	m=read();
	rep(i,1,m) q[i].l=read(),q[i].r=read(),q[i].id=i;
	dicon(1,n,1,m);
	rep(i,1,m) print(ans[i],'\n');
	return 0;
}
posted on 2021-03-04 22:48  Oxide  阅读(108)  评论(0编辑  收藏  举报