题目
解法
无论多么麻烦的代码写完过后都要耐着性子检查。
首先有朴素 \(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;
}