P3722 [AH2017/HNOI2017] 影魔
题目背景
影魔,奈文摩尔,据说有着一个诗人的灵魂。事实上,他吞噬的诗人灵魂早已成千上万。
千百年来,他收集了各式各样的灵魂,包括诗人、牧师、帝王、乞丐、奴隶、罪人,当然,还有英雄。
每一个灵魂,都有着自己的战斗力,而影魔,靠这些战斗力提升自己的攻击。
题目描述
奈文摩尔有 \(n\) 个灵魂,他们在影魔宽广的体内可以排成一排,从左至右标号 \(1\) 到 \(n\)。第 \(i\) 个灵魂的战斗力为 \(k_i\),灵魂们以点对的形式为影魔提供攻击力。对于灵魂对 \(i, j\ (i<j)\) 来说,若不存在 \(k_s\ (i<s<j)\) 大于 \(k_i\) 或者 \(k_j\),则会为影魔提供 \(p_1\) 的攻击力。另一种情况,令 \(c\) 为 \(k_{i + 1}, k_{i + 2}, \cdots, k_{j -1}\) 的最大值,若 \(c\) 满足:\(k_i < c < k_j\),或者 \(k_j < c < k_i\),则会为影魔提供 \(p_2\) 的攻击力,当这样的 \(c\) 不存在时,自然不会提供这 \(p_2\) 的攻击力;其他情况的点对,均不会为影魔提供攻击力。
影魔的挚友噬魂鬼在一天造访影魔体内时被这些灵魂吸引住了,他想知道,对于任意一段区间 \([a, b]\),位于这些区间中的灵魂对会为影魔提供多少攻击力,即考虑所有满足 \(a\le i<j\le b\) 的灵魂对 \(i, j\) 提供的攻击力之和。
顺带一提,灵魂的战斗力组成一个 \(1\) 到 \(n\) 的排列:\(k_1, k_1, \cdots, k_n\)。
提示
对于 \(100\%\) 的数据,\(1\le n,m\le 200000, 1\le p_1, p_2\le 1000\)。
Solution:
题意简述:给定一个排列 \({k_n}\) ,对于 \(i<c \le j\) 的三元组有以下两类贡献:
\(p_1\): \(k_i,k_j\) 有一个是区间最大值,另一个是次大值
\(p_2\): \(min(k_i,k_j)<k_c<max(k_i,k_j)\)
我们考虑对一个点 \(pos\) 分别求出左右两边第一个比它大的 \(k\) 的下标 \(L,R\) 可以用单调栈在 \(O(n)\) 解决
然后我们考虑如何刻画这个三元组的贡献:
\(k_L,k_R\) ,\(k_i,k_{i+1}\) 显然分别对区间 \([L,R]\) ,\([i,i+1]\) 有 \(p_1\) 的贡献
\(L,R,pos\) 显然满足一个东西
$\forall i\in[R+1,i-1] R>k_{pos}>p_i $
\(\forall i\in[i+1,R-1] L>k_{pos}>p_i\)
所以:
\(\forall i\in[i+1,R-1]\) 会在 \([L,i]\) 上产生 \(p_2\) 的贡献
\(\forall i\in[R+1,i-1]\) 会在 \([i,R]\) 上产生\(p_2\) 的贡献
这可太线段树了
我们开一颗主席树,以每个区间的左端点作为 \(rt\) 然后线段树上的下标对应右端点的区间,然后我们的贡献就可以这样维护:
if(i<n)E[i].emplace_back(i+1,i+1,p1);
if((1<=l)&&(r<=n))E[l].emplace_back(r,r,p1);
if((1<=l)&&(i+1<=r-1))E[l].emplace_back(i+1,r-1,p2);
if((l+1<=i-1)&&(r<=n))E[r].emplace_back(l+1,i-1,p2);
然后每次查询就是
ans=T.query(T.rt[l-1],T.rt[r],1,n,l,r);
然后注意一个东西,由于我的主席树的下标是挂在询问上的,所以最好写标记永久化,不然不好写 \(pushdown\)
Code
#include<bits/stdc++.h>
#define int long long
const int N=2e5+5;
using namespace std;
//Segment_Tree
struct Segment_Tree{int cnt;int rt[N];struct Tree{int ls,rs,val,tag;}t[N*64];void insert(int &x,int y,int l,int r,int L,int R,int k){t[x=++cnt]=t[y];int len=(-max(L,l)+min(r,R)+1);t[x].val+=len*k;if(L<=l&&r<=R){t[x].tag+=k;return ;}int mid=l+r>>1;if(L<=mid)insert(t[x].ls,t[y].ls,l,mid,L,R,k);if(mid<R) insert(t[x].rs,t[y].rs,mid+1,r,L,R,k);}int query(int x,int y,int l,int r,int L,int R){if(!y)return 0;int len=(-max(L,l)+min(r,R)+1);if(L<=l&&r<=R){return (-t[x].val+t[y].val);}int mid=l+r>>1,res=0;if(L<=mid)res+=query(t[x].ls,t[y].ls,l,mid,L,R);if(mid<R) res+=query(t[x].rs,t[y].rs,mid+1,r,L,R);res+=len*(-t[x].tag+t[y].tag);return res;}
}T;
int n,m,p1,p2;
int a[N],st[N],L[N],R[N];
vector<tuple<int,int,int> >E[N];
void work()
{cin>>n>>m>>p1>>p2;for(int i=1;i<=n;i++){scanf("%lld",&a[i]);while(st[0]&&a[st[st[0]]]<a[i]){R[st[st[0]]]=i;st[0]--;}L[i]=st[st[0]];st[++st[0]]=i;}while(st[0]){R[st[st[0]]]=n+1;st[0]--;}for(int i=1;i<=n;i++){int l=L[i],r=R[i];if(i<n)E[i].emplace_back(i+1,i+1,p1);if((1<=l)&&(r<=n))E[l].emplace_back(r,r,p1);if((1<=l)&&(i+1<=r-1))E[l].emplace_back(i+1,r-1,p2);if((l+1<=i-1)&&(r<=n))E[r].emplace_back(l+1,i-1,p2);}for(int i=1;i<=n;i++){T.rt[i]=T.rt[i-1];for(auto [l,r,w] : E[i]){T.insert(T.rt[i],T.rt[i],1,n,l,r,w);}}for(int i=1,l,r;i<=m;i++){scanf("%lld%lld",&l,&r);int ans=T.query(T.rt[l-1],T.rt[r],1,n,l,r);printf("%lld\n",ans);}
}
#undef int
int main()
{//freopen("P3722.in","r",stdin);freopen("P3722.out","w",stdout);work();return 0;
}