[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;
}
posted @ 2021-10-26 20:06  zkdxl  阅读(87)  评论(1编辑  收藏  举报