BZOJ4540: [Hnoi2016]序列(洛谷P3246)

本文详细介绍了如何使用莫队算法结合ST表和单调栈解决区间查询问题,通过预处理最小值及其贡献,实现高效的区间更新和查询操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

莫队 ST表 单调栈

BZOJ题目传送门
洛谷题目传送门

考虑添加 r + 1 r+1 r+1 [ l , r ] [l,r] [l,r]中产生的贡献。显然多了 r − l + 2 r-l+2 rl+2个区间。设 [ l , r + 1 ] [l,r+1] [l,r+1]的最小值取在 p p p点,那么 p p p产生的贡献为 w [ p ] ∗ ( p − l + 1 ) w[p]*(p-l+1) w[p](pl+1),剩下的贡献可以前缀和搞一搞。 p p p可以ST表预处理后直接得到。对每个位置预处理以它为最小值的区间 [ L , R ] [L,R] [L,R],记两个前缀和 s l [ i ] sl[i] sl[i] s r [ i ] sr[i] sr[i]分别表示 [ 1 , i ] [1,i] [1,i]的前缀和贡献和 [ i , n ] [i,n] [i,n]的后缀和贡献。那么有 s l [ i ] = s l [ L i − 1 ] + w [ i ] ∗ ( i − L i + 1 ) sl[i]=sl[L_i-1]+w[i]*(i-L_i+1) sl[i]=sl[Li1]+w[i](iLi+1),另一个同样。这样就可以用莫队维护了。

代码:

#include<cctype>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100005
#define F inline
using namespace std;
typedef long long LL;
struct query{ int l,r,id,bl,br; }q[N];
int n,m,k,tp,p,lg[N],L[N],R[N],stk[N],ST[N][18];
LL s,a[N],sl[N],sr[N],ans[N];
F char readc(){
	static char buf[100000],*l=buf,*r=buf;
	if (l==r) r=(l=buf)+fread(buf,1,100000,stdin);
	return l==r?EOF:*l++;
}
F int _read(){
	int x=0,f=1; char ch=readc();
	while (!isdigit(ch)) ch=='-'?f=-1:1,ch=readc();
	while (isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48),ch=readc();
	return x*f;
}
F bool cmp(query a,query b){ return a.bl==b.bl?(a.br<b.br)^(a.bl&1):a.bl<b.bl; }
#define Min(x,y) (a[x]<a[y]?x:y)
F void Make(){
	for (int j=1,k=log2(n);j<=k;j++)
		for (int i=1;i<=n-(1<<j)+1;i++)
			ST[i][j]=Min(ST[i][j-1],ST[i+(1<<j-1)][j-1]);
}
#define srch(l,r) Min(ST[l][lg[r-l+1]],ST[r-(1<<lg[r-l+1])+1][lg[r-l+1]])
#define nsrtl(l,r,f) p=srch(l,r),s+=1ll*f*(a[p]*(r-p+1)+sr[l]-sr[p])
#define nsrtr(l,r,f) p=srch(l,r),s+=1ll*f*(a[p]*(p-l+1)+sl[r]-sl[p])
int main(){
	n=_read(),m=_read(),k=sqrt(n);
	for (int i=1;i<=n;i++) ST[i][0]=i,a[i]=_read(),lg[i]=log2(i);
	Make(),L[1]=1,stk[++tp]=1,R[n]=n;
	for (int i=2;i<=n;i++){
		while (tp&&a[stk[tp]]>a[i]) R[stk[tp--]]=i-1;
		L[i]=stk[tp]+1,stk[++tp]=i;
	}
	for (int i=1;i<=tp;i++) R[stk[i]]=n;
	for (int i=1;i<=n;i++) sl[i]=sl[L[i]-1]+a[i]*(i-L[i]+1);
	for (int i=n;i;i--) sr[i]=sr[R[i]+1]+a[i]*(R[i]-i+1);
	for (int i=1,l,r;i<=m;i++)
		l=_read(),r=_read(),q[i]=(query){l,r,i,(l-1)/k+1,(r-1)/k+1};
	sort(q+1,q+m+1,cmp); int l=1,r=0;
	for (int i=1;i<=m;i++){
		while (r<q[i].r) r++,nsrtr(l,r,1);
		while (l>q[i].l) l--,nsrtl(l,r,1);
		while (r>q[i].r) nsrtr(l,r,-1),r--;
		while (l<q[i].l) nsrtl(l,r,-1),l++;
		ans[q[i].id]=s;
	}
	for (int i=1;i<=m;i++) printf("%lld\n",ans[i]);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值