*题解:P3722 [AHOI2017/HNOI2017] 影魔

原题链接

解析

对于提供 \(p_1\) 攻击力的情况,有可能是 \(j = i + 1\),也有可能是 \(\max_{t=i + 1}^{j - 1}k_t<\min(k_i,k_j)\)。前者的贡献可以直接计算,考虑后者的贡献怎么求。枚举 \(t\),则 \(k_i\) 必定为 \(t\) 左侧第一个比 \(k_t\) 大的数,\(k_j\) 必定为 \(t\) 右侧第一个比 \(k_t\) 大的数,不妨记这样的 \(i,j\) 分别为 \(l_t,r_t\)。于是我们就得到了 \(O(n)\) 个做 \(p_1\) 贡献的合法数对 \((l_t,r_t)\)。询问就相当于求被区间 \([a,b]\) 包含的区间个数。做法是将询问按离线然后从左往右扫右端点并维护一个树状数组。当询问区间为 \([a,b]\) 时,对于数对 \((l_t,r_t)\),当扫到 \(b \ge r_t\) 时,其在 \(l_t\) 处有贡献,最终 \([a,b]\) 的贡献和即为所求。

对于提供 \(p_2\) 攻击力的情况,有对称的两种情况,先只考虑 \(k_i<k_t<k_j\) 的情况。同样枚举 \(t\),则 \(k_j\)\(k_{r_t}\)\(k_i\) 可以为 \(k_{l_t + 1}\)\(k_{t - 1}\)。于是,当询问区间为 \([a,b]\) 时,对于数对 \((l_t + 1,r_t)\),当扫到 \(b \ge r_t\) 时,其在 \([l_t + 1,t - 1]\) 处有贡献,最终 \([a,b - 1]\) 的贡献和即为所求。

需要区间加,区间求和,所以使用线段树。

同理可处理对称的情况,注意要倒着扫。

时间复杂度 \(O((n + m) \log n)\)

代码

#include <bits/stdc++.h>
#define ls(x) ((x) << 1)
#define rs(x) ((x) << 1 | 1)
#define mid (l + r >> 1)
using namespace std;
const int N = 200000 + 5,M = 17;
typedef long long ll;
typedef pair<int,int> pii;
int k[N],l[N],r[N];
struct SMT{
	ll sum[N << 2],tag[N << 2];
	void push_up(int p){
		sum[p] = sum[ls(p)] + sum[rs(p)];
	}
	void add_tag(int p,int l,int r,ll k){
		tag[p] += k;
		sum[p] += (r - l + 1) * k;
	}
	void push_down(int p,int l,int r){
		if(!tag[p]) return;
		add_tag(ls(p),l,mid,tag[p]),add_tag(rs(p),mid + 1,r,tag[p]);
		tag[p] = 0;
	}
	void add(int p,int l,int r,int L,int R,ll k){
		if(l > R || r < L) return;
		if(l >= L && r <= R){
			add_tag(p,l,r,k);
			return;
		}
		push_down(p,l,r);
		add(ls(p),l,mid,L,R,k),add(rs(p),mid + 1,r,L,R,k);
		push_up(p);
	}
	ll ask(int p,int l,int r,int L,int R){
		if(l > R || r < L) return 0;
		if(l >= L & r <= R){
			return sum[p];
		}
		push_down(p,l,r);
		return ask(ls(p),l,mid,L,R) + ask(rs(p),mid + 1,r,L,R);
	}
}tr[2];
ll res[N];
struct Modify{
	int l,r,v;
};
vector<Modify> mol[N],mor[N];
vector<pii> ql[N],qr[N];
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
//	freopen("in.txt","r",stdin);
//	freopen("out.txt","w",stdout);
	int n,m,p1,p2;
	cin>>n>>m>>p1>>p2;
	stack<int> s;
	for(int i=1;i<=n;i++){
		cin>>k[i];
		while(!s.empty() && k[i] > k[s.top()]){
			r[s.top()] = i;
			s.pop();
		}
		s.push(i);
	}
	while(!s.empty()){
		r[s.top()] = n + 1;
		s.pop();
	}
	for(int i=n;i>=1;i--){
		while(!s.empty() && k[i] > k[s.top()]){
			l[s.top()] = i;
			s.pop();
		}
		s.push(i);
	}
	while(!s.empty()){
		l[s.top()] = 0;
		s.pop();
	}
	for(int i=1;i<=n;i++){
		if(l[i]) mor[r[i]].push_back({l[i],l[i],p1});
		if(r[i] - 1 > i) mol[l[i]].push_back({i + 1,r[i] - 1,p2});
		if(l[i] + 1 < i) mor[r[i]].push_back({l[i] + 1,i - 1,p2});
	}
	for(int i=1;i<=m;i++){
		int a,b;
		cin>>a>>b;
		res[i] += 1ll * (b - a) * p1; 
		qr[b].push_back({a,i});
		ql[a].push_back({b,i});
	}
	for(int i=1;i<=n;i++){
		for(int j=0;j<mor[i].size();j++){
			tr[0].add(1,1,n,mor[i][j].l,mor[i][j].r,mor[i][j].v);
		}
		for(int j=0;j<qr[i].size();j++){
			res[qr[i][j].second] += tr[0].ask(1,1,n,qr[i][j].first,i - 1);
		}
	}
	for(int i=n;i>=1;i--){
		for(int j=0;j<mol[i].size();j++){
			tr[1].add(1,1,n,mol[i][j].l,mol[i][j].r,mol[i][j].v);
		}
		for(int j=0;j<ql[i].size();j++){
			res[ql[i][j].second] += tr[1].ask(1,1,n,i + 1,ql[i][j].first);
		}
	}
	for(int i=1;i<=m;i++){
		cout<<res[i]<<'\n';
	}
	return 0;
}
posted @ 2026-04-18 17:49  yutar  阅读(8)  评论(0)    收藏  举报