#ST表,单调栈#洛谷 5648 Mivik的神力

题目


分析

考虑答案应该是一段单调不下降的序列,
考虑预处理出每个点往后第一个大于这个点的位置,
那么答案应该是左端点到区间内最大的位置以及这个位置到右端点的贡献
那么区间最大的位置可以用ST表做,然后前面的贡献用后缀和,
后面的贡献也就似乎这个位置的值乘上后面的长度,记得开long long


代码

#include <cstdio>
#include <cctype>
#include <stack>
#define rr register
using namespace std;
const int N=500011; typedef long long lll; stack<int>stac;
int n,m,two[19],lg[N],a[N],f[N][19],fa[N]; lll s[N];
inline signed iut(){
	rr int ans=0; rr char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
	return ans; 
}
inline void print(lll ans){
	if (ans>9) print(ans/10);
	putchar(ans%10+48);
}
signed main(){
	n=iut(),m=iut(),two[0]=1,lg[0]=-1;
	a[n+1]=2e9,stac.push(n+1),fa[n+1]=n+1;
	for (rr int i=1;i<19;++i) two[i]=two[i-1]<<1;
	for (rr int i=1;i<=n;++i) a[i]=iut(),lg[i]=lg[i>>1]+1;
	for (rr int i=n;i;--i){
		while (a[i]>=a[stac.top()]) stac.pop();
		fa[i]=stac.top(),stac.push(i);
	}
	for (rr int i=n;i;--i) s[i]=s[fa[i]]+1ll*(fa[i]-i)*a[i];
	for (rr int i=1;i<=n;++i) f[i][0]=i;
	for (rr int j=1;j<19;++j)
	for (rr int i=1;i+two[j]-1<=n;++i){
		rr int z1=f[i][j-1],z2=f[i+two[j-1]][j-1];
		if (a[z1]<a[z2]) f[i][j]=z2; else f[i][j]=z1;
	}
	rr lll lans=0;
	for (rr int i=1;i<=m;++i,putchar(10)){
		rr int l=(iut()^lans)%n+1,r=l+(iut()^(lans+1))%(n-l+1);
		rr int now,z=lg[r-l+1],z1=f[l][z],z2=f[r-two[z]+1][z];
		if (a[z1]<a[z2]) now=z2; else now=z1;
		print(lans=s[l]-s[now]+1ll*a[now]*(r-now+1));
	}
	return 0;
} 
posted @ 2020-11-03 21:13  lemondinosaur  阅读(147)  评论(0编辑  收藏  举报