前言
如何评价题解看不懂只能自己把这道题想出来。
明明就是懒得看题解啊喂!(#`O′)
个人感觉这个应该不算 ( t cdq)分治,但是又不知道怎么分类,于是。。。
题目
讲解
分治其实就是按一定顺序做就可以保证时间复杂度的暴力。
所以我们先考虑 (O(n^2)) 的 ( t dp),显然我们可以令 (dp_{l,r}) 表示区间 ([l,r]) 的最大划分。
考虑优化。
我们注意到 (Lle 50),这给了我们乱搞的资本。
考虑将询问丢到各个区间里面分治,如果一个询问完全被一个大区间 ([l,r]) 的子区间包含,给这个子区间就好。这里我们认为 ([l,r]) 的子区间为 ([l,mid]) 和 ([mid+1,r]) ,和线段树是一样的。
如果这个询问跨越了中间的边界 (mid) 怎么办?由于 (L) 很小,我们可以枚举它在左边的长度,当然在右边的长度也可以计算出来。中间越界的部分是一个长度为 (L) 的区间加,两边是可以预处理的一个 ( t dp)。
思路不难,细节有点多。
代码
//12252024832524
#include <cstdio>
#include <cstring>
#include <algorithm>
#define TT template<typename T>
using namespace std;
typedef long long LL;
const int MAXN = 100005;
const int MAXL = 52;
int n,len,Q;
int a[MAXN],ans[MAXN];
LL Read()
{
LL x = 0,f = 1;char c = getchar();
while(c > '9' || c < '0'){if(c == '-')f = -1;c = getchar();}
while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
return x * f;
}
TT void Put1(T x)
{
if(x > 9) Put1(x/10);
putchar(x%10^48);
}
TT void Put(T x,char c = -1)
{
if(x < 0) putchar('-'),x = -x;
Put1(x); if(c >= 0) putchar(c);
}
TT T Max(T x,T y){return x > y ? x : y;}
TT T Min(T x,T y){return x < y ? x : y;}
TT T Abs(T x){return x < 0 ? -x : x;}
struct Query
{
int l,r,ID;
}q[MAXN],lq[MAXN],rq[MAXN];
int dpl[MAXL][MAXN],dpr[MAXL][MAXN];//这里的l,r指的是左边的区间和右边的区间
//右边界空出i,j开始dp [j,mid-i];左边界空出i,j开始dp [mid+1+i,j]
void workl(int l,int r)
{
for(int i = 0;i < len && r-i >= l;++ i)//细品这里取等的原因
{
int R = r-i;
for(int j = R+1;j >= l;-- j)
{
if(R-j+1 < len) dpl[i][j] = 0;
else dpl[i][j] = Max(dpl[i][j+1],dpl[i][j+len] + a[j+len-1]);
}
}
}
void workr(int l,int r)
{
for(int i = 0;i < len && l+i <= r;++ i)
{
int L = l+i;
for(int j = L-1;j <= r;++ j)
{
if(j-L+1 < len) dpr[i][j] = 0;
else dpr[i][j] = Max(dpr[i][j-1],dpr[i][j-len] + a[j]);
}
}
}
void solve(int l,int r,int ql,int qr)
{
if(ql > qr || r-l+1 < len) return;
if(l == r)
{
for(int i = ql;i <= qr;++ i) ans[q[i].ID] = Max(a[l],0);
return;
}
int mid = (l+r) >> 1,ln = 0,rn = 0;
workl(l,mid); workr(mid+1,r);
for(int i = ql;i <= qr;++ i)
{
if(q[i].r <= mid) lq[++ln] = q[i];
else if(q[i].l > mid) rq[++rn] = q[i];
else
{
ans[q[i].ID] = Max(0,dpl[0][q[i].l]+dpr[0][q[i].r]);
int LL = Max(0,mid+len-q[i].r),RR = Min(len,mid-q[i].l+1);
for(int j = LL;j <= RR;++ j)//枚举左边剩多少
ans[q[i].ID] = Max(ans[q[i].ID],dpl[j][q[i].l] + dpr[len-j][q[i].r] + a[mid+len-j]);
}
}
for(int i = 1;i <= ln;++ i) q[ql+i-1] = lq[i];
for(int i = 1;i <= rn;++ i) q[ql+ln+i-1] = rq[i];
solve(l,mid,ql,ql+ln-1); solve(mid+1,r,ql+ln,ql+ln+rn-1);
}
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
n = Read(); len = Read();
for(int i = 1;i <= n;++ i) a[i] = Read() + a[i-1];
for(int i = n;i > len;-- i) a[i] -= a[i-len];
Q = Read();
for(int i = 1;i <= Q;++ i)
{
q[i].l = Read();
q[i].r = Read();
q[i].ID = i;
}
solve(1,n,1,Q);
for(int i = 1;i <= Q;++ i) Put(ans[i],'
');
return 0;
}