LOJ #2051. 「HNOI2016」序列(单调栈)

题目

为啥会有一个莫队的标签啊
求出每个点左/右边第一个比它小的点的位置。(单调栈维护)
然后连边会分别构成两个森林。
在森林中统计链上信息即可求出右端点为 R R R左端点 > = L >=L >=L的答案之和,具体就是链上祖先和然后再求区间最小值之后差分。
再来一个前缀和就可以得到 [ L , R ] [L,R] [L,R]的区间答案。

A C   C o d e \rm AC\ Code AC Code

#include<bits/stdc++.h>
#define maxn 100005
#define lim 17
#define S 300
#define LL long long
#define inf 0x3f3f3f3f
using namespace std;

int n,q,a[maxn],sta[maxn],prel[maxn],prer[maxn],lg[maxn];
LL ans[maxn],fl[maxn],fr[maxn],gl[maxn],gr[maxn];
int st[lim][maxn];
int qry(int L,int R){ int t=lg[R-L+1];return a[st[t][L]] < a[st[t][R-(1<<t)+1]] ? st[t][L] : st[t][R-(1<<t)+1]; }
int main(){
//	freopen("1.in","r",stdin);
	scanf("%d%d",&n,&q);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]),st[0][i] = i,(i>1?lg[i]=lg[i>>1]+1:0);
	for(int j=1;j<lim;j++) for(int i=1;i+(1<<j)-1<=n;i++) st[j][i] = a[st[j-1][i]] < a[st[j-1][i+(1<<j-1)]] ? st[j-1][i] : st[j-1][i+(1<<j-1)];
	a[0] = a[n+1] = -inf;
	sta[sta[0] = 1] = 0;
	for(int i=1;i<=n;i++){
		for(;sta[0] && a[sta[sta[0]]]>=a[i];sta[0]--);
		prel[i] = sta[sta[0]],fl[i] = fl[prel[i]] + 1ll * a[i] * (i - prel[i]);
		gl[i] = gl[i-1] + fl[i];
		sta[++sta[0]] = i;
	}
	sta[sta[0] = 1] = n+1;
	for(int i=n;i>=1;i--){
		for(;sta[0] && a[sta[sta[0]]]>=a[i];sta[0]--);
		prer[i] = sta[sta[0]],fr[i] = fr[prer[i]] + 1ll * a[i] * (prer[i] - i);
		gr[i] = gr[i+1] + fr[i];
		sta[++sta[0]] = i;
	}
	for(int i=1,L,R;i<=q;i++){ scanf("%d%d",&L,&R);int p = qry(L,R);
		printf("%lld\n",gr[L]+gl[R]-gl[p]-gr[p]-((R-p)*fl[p]+(p-L)*fr[p])+(p-L+1ll)*(R-p+1ll)*a[p]);
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值