[CF1601E] Phys Ed Online
\(\text{Problem}:\)Phys Ed Online
\(\text{Solution}:\)
设 \(b_{i}=\min\{a_{j}\},i-k\leq j\leq i\),可以得到一个区间的 \([l,r]\) 答案形式为 \(a_{l}+\sum\limits_{i=1}^{t}\min\{b_{l+jk}\},1\leq j\leq i\),\(t\) 为某个上界。注意到上式中所有下标在模 \(k\) 意义下都是同余的,这提示我们对询问按照 \(l\% k\) 的值分组,进行分组计算。
在组内维护 \(f_{i}\) 表示左端点为 \(i\) 时的后缀答案。考虑新加入的 \(b_{i}\) 会有多少贡献,不难发现是找到离 \(i\) 最近满足 \(b_{j}<b_{i}\) 的位置,有 \(f_{i}=f_{j}+(j-i)b_{p_{i}}\)(\(p_{i}\) 表示对组内第 \(i\) 个元素在原序列中出现位置)。这只需维护一个严格单调递减的序列,利用单调栈即可实现。问题转化为利用 \(f_{i}\) 快速求出每个询问的答案。
记 \(g_{l,r}=\min\{b_{k}\},l\leq k\leq r\),得 \(f_{i}=\sum\limits_{j=i}^{up}g_{i,j}\),而对于一组询问要求的是 \(\sum\limits_{j=i}^{t}g_{i,j}\)。对此,如果我们能在区间 \([l,r]\) 中找到一个位置 \(s\),满足 \(g_{l,i}=g_{s,i},i\leq s\leq r\),并且当 \(s>l\) 时 \([s,r]\) 中找不到这样的位置,就可以利用差分统计出答案。不难发现令 \(s\) 为区间 \([l,r]\) 中 \(b_{i}\) 最小的位置就满足条件。
总时间复杂度 \(O(n\log n+q)\)。
\(\text{Code}:\)
#include <bits/stdc++.h>
#pragma GCC optimize(3)
#define int long long
#define ri register
#define mk make_pair
#define fi first
#define se second
#define pb push_back
#define eb emplace_back
#define is insert
#define es erase
#define vi vector<int>
#define vpi vector<pair<int,int>>
using namespace std; const int N=300010, M=20;
inline int read()
{
int s=0, w=1; ri char ch=getchar();
while(ch<'0'||ch>'9') { if(ch=='-') w=-1; ch=getchar(); }
while(ch>='0'&&ch<='9') s=(s<<3)+(s<<1)+(ch^48), ch=getchar();
return s*w;
}
int n,Q,K,a[N],b[N];
int mi[N][M],pos[N][M],lg[N];
int f[N],pc[N],m,sta[N],tp;
vpi q[N]; int ans[N];
inline int Min(int l,int r)
{
int k=lg[r-l+1];
return min(mi[l][k],mi[r-(1<<k)+1][k]);
}
inline int Minp(int l,int r)
{
int k=lg[r-l+1];
if(mi[l][k]<=mi[r-(1<<k)+1][k]) return pos[l][k];
return pos[r-(1<<k)+1][k];
}
signed main()
{
n=read(), Q=read(), K=read();
for(ri int i=1;i<=n;i++) a[i]=read(), mi[i][0]=a[i];
lg[0]=-1; for(ri int i=1;i<N;i++) lg[i]=lg[i>>1]+1;
for(ri int i=1;(1<<i)<=n;i++)
for(ri int j=1;j+(1<<i)-1<=n;j++)
mi[j][i]=min(mi[j][i-1],mi[j+(1<<(i-1))][i-1]);
for(ri int i=K;i<=n;i++) b[i]=Min(i-K,i);
for(ri int i=1;i<=Q;i++)
{
int l,r;
l=read(), r=read();
q[l].eb(mk(r,i));
}
for(ri int i=0;i<K;i++)
{
m=tp=0;
for(ri int j=(!i)?(i+K):(i);j<=n;j+=K) pc[++m]=j, f[m]=0;
sta[0]=m+1, f[m+1]=0;
for(ri int j=1;j<=m;j++) mi[j][0]=b[pc[j]], pos[j][0]=j;
for(ri int j=1;(1<<j)<=m;j++)
for(ri int k=1;k+(1<<j)-1<=m;k++)
{
if(mi[k][j-1]<=mi[k+(1<<(j-1))][j-1]) mi[k][j]=mi[k][j-1], pos[k][j]=pos[k][j-1];
else mi[k][j]=mi[k+(1<<(j-1))][j-1], pos[k][j]=pos[k+(1<<(j-1))][j-1];
}
for(ri int j=m;j;j--)
{
while(tp && b[pc[sta[tp]]]>=b[pc[j]]) tp--;
f[j]=f[sta[tp]]+(sta[tp]-j)*b[pc[j]];
sta[++tp]=j;
}
for(ri int j=1;j<=m;j++)
for(auto k:q[pc[j]])
{
int l=j+1,r=j+(k.fi-pc[j])/K;
ans[k.se]=a[pc[j]];
if(l>r) continue;
int t=Minp(l,r);
ans[k.se]+=f[l]-f[t]+(r-t+1)*b[pc[t]];
}
}
for(ri int i=1;i<=Q;i++) printf("%lld\n",ans[i]);
return 0;
}