本质上就是找两个长度为 k k k的窗口算贡献。
考虑先固定第一个窗口位于 [ L , R ] ( R − L + 1 = k ) [L,R](R-L+1=k) [L,R](R−L+1=k),然后暴力遍历 ( l i , r i ) (l_i,r_i) (li,ri),考虑它们对第一个窗口的总贡献为 s m sm sm,然后考虑第二个窗口位于每个位置上的时候对应的附加贡献, s m sm sm和附加贡献加起来后取 m a x max max即可。
本题的难点在于如何快速计算
(
l
i
,
r
i
)
(l_i,r_i)
(li,ri)对于第二个窗口在每个位置上的贡献。这里运用差分的方法。对于特定的
(
l
,
r
)
(l,r)
(l,r)而言,假设第一个窗口
[
L
,
R
]
[L,R]
[L,R]对它的覆盖区间长度是
w
w
w,那么当第二个窗口与
(
l
,
r
)
(l,r)
(l,r)的覆盖区间长度小于等于
w
w
w的时候我们可以认为
(
l
,
r
)
(l,r)
(l,r)对第二个窗口的附加贡献是0,但是
>
w
>w
>w的时候我们就需要考虑对于第二个窗口的附加贡献了。
考虑枚举第二个窗口的右端点从小到大,根据滑动窗口的思想,每次滑动一格位置都需要考虑一下新增加的贡献和要删除的贡献。不过这些贡献来自于
(
l
i
,
r
i
)
(l_i,r_i)
(li,ri),故干脆考虑
(
l
i
,
r
i
)
(l_i,r_i)
(li,ri)会对第二个窗口在哪些位置产生贡献即可,进行区间加减操作,不过区间加可以改成修改差分数组。
具体来说,对于 ( l , r ) (l,r) (l,r)而言,需要考虑当第二个滑动窗口位于 R ′ R' R′位置(正在接近 ( l , r ) (l,r) (l,r))的时候,会对它多产生的附加贡献是多少,以及当第二个滑动窗口将要离开 ( l , r ) (l,r) (l,r)的时候,对它的附加贡献又会减少多少,体现在差分数组上就是某些位置的加减关系。差分数组被预处理好了之后,扫一遍差分数组相当于就是模拟第二个滑动窗口的扫动过程,再此过程中计算前缀和并加到附加贡献上即可,同时还要不断跟答案取max。
int n,m,k,al[maxn],ar[maxn],val[maxn];
int main(){
n=rd(),m=rd(),k=rd();
FOR(i,0,m)al[i]=rd(),ar[i]=rd();
int ans=0;
FOR(i,k,n+1){
int as=0,L=i-k+1,R=i;
FOR(j,0,m){
int l=al[j],r=ar[j],tt=0;
as+=(tt=max(min(r,R)-max(L,l)+1,0));
if(min(r-l+1,k)>tt){
val[l+tt]++;
val[min(l+k,r+1)]--;
val[max(r+1,l+k)]--;
val[r-tt+1+k]++;
}
}
int pre=0;
FOR(j,1,n+1){
pre+=val[j];
as+=pre;
ans=max(ans,as);
val[j]=0;
}
}
wrn(ans);
}