影魔|树状数组|题解

这个题能够想到去找它左侧第一个大于它的数位置li和右侧第一个大于它的数位置ri,然后统计。
但在如何统计上就有大问题。
假设我现在在一个点i,向右找到li这个时候有(li,i]之间的所有r<i的给p2的贡献,等于i的给p1贡献。
同时li给出p1贡献,[1,li1]所有r>i的给出p2贡献。
这时发现如果题目要求求的是前缀[1,n]这种形式的答案,可以用树状数组直接统计求出。
但它不是,于是想了想这个明显就离线处理,那前缀后缀都出来,想办法结合成答案?
然后这个方式就没有前途了,(可能是我菜,推不出来。
被迫看题解,然后发现确实是需要换一种统计方式。
考虑i定住的时候,看两边的贡献是有三种。
1.li,ri这个可以贡献p1
2.[li<L<i],ri这个贡献p2
3.li,[i<R<ri]这个依旧是p2
这样统计也是不重不漏的,然而依旧有怎么处理答案统计的问题。
我们把每个i的li,ri处均打上它对应要修改的区间及答案即可,1、2均给ri统计,3给li,这样就都是区间修可解决的了。
这时发现它好处就在于这个li,ri它被定下来了,你所有的贡献在某个端点向右或向左的方向也定下来了。
而且它能够进行对应位置的区间修改,而非不知位置的答案统计。
上一个方法答案为什么难以统计,就是在于它把一个贡献对应的位置关系省去了其一(因为是直接树状数组上去做)。
那你省去了位置关系的情况下,所有的r端点固定的答案会在一个位置加入,那就难以统计区间和。
回来看正解方案,当需要统计的是[L,R]之间的答案时,我们在顺序访问到L的时候,统计一下[L,R]之间的答案。
在访问到R时再统计一次,两次相减即可。

#include<bits/stdc++.h>
typedef long long ll;
typedef unsigned long long ull;
#define qr qr()
#define ps push_back
#define pa pair<int,int>
#define ve vector
#define fi first
#define se second
using namespace std;
const int N=2e5+200;
int n,m,p1,p2,top,sta[N],a[N],l[N],r[N];
ll ans[N];
inline ll qr{
	ll x=0;char ch=getchar();bool f=0;
	while(ch>57||ch<48)f=(ch=='-')?1:0,ch=getchar();
	while(ch>=48&&ch<=57)x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	return f?-x:x;
}
struct ak{int l,r,id;};
struct BIT{
	ll c1[N+200],c2[N+200];
	void update(int pos,int x){
		ll u2=x*pos;
		while(pos<=n)c1[pos]+=x,c2[pos]+=u2,pos+=pos&-pos;
	}
	ll ask(int pos){
		ll ans=0,tmp=pos+1;
		while(pos)ans+=c1[pos]*tmp-c2[pos],pos-=pos&-pos;
		return ans;
	}
}T;
struct cha{int l,r,v;};
ve<cha>ch[N];
ve<ak>q1[N],q2[N];
void init(){
	n=qr;m=qr;p1=qr;p2=qr;
	for(int i=1;i<=n;++i)a[i]=qr;
	for(int i=1,l,r;i<=m;++i)l=qr,r=qr,q1[l].ps({l,r,i}),q2[r].ps({l,r,i});
	a[0]=a[n+1]=n+1;
	for(int i=1;i<=n+1;++i){
		while(a[sta[top]]<a[i])r[sta[top]]=i,--top;
		l[i]=sta[top];sta[++top]=i;
	}for(int i=1;i<=n;++i){
		// cout<<i<<' '<<l[i]<<' '<<r[i]<<endl;
		ch[l[i]].ps({i+1,r[i],p2});
		ch[l[i]].ps({r[i],r[i]+1,p1});
		ch[r[i]].ps({l[i]+1,i,p2});
	}
	for(int i=1;i<=n;++i){
		for(auto k:q1[i])ans[k.id]-=T.ask(k.r)-T.ask(k.l-1);
		for(auto k:ch[i])T.update(k.l,k.v),T.update(k.r,-k.v);
		T.update(i+1,p1);T.update(i+2,-p1);//相邻两点的贡献别忘了加入.
		for(auto k:q2[i])ans[k.id]+=T.ask(k.r)-T.ask(k.l-1);
	}// cout<<"kinerk"<<endl;
	for(int i=1;i<=m;++i)cout<<ans[i]<<'\n';
}
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);




	// freopen("in.in","r",stdin);
	// freopen("out.out","w",stdout);




	init();
	return 0;
}
posted @   SLS-wwppcc  阅读(12)  评论(2编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示