【题解】P3722 [AHOI2017 HNOI2017] 影魔
P3722 [AHOI2017/HNOI2017] 影魔
题意
给定长度为 \(n\) 的排列 \(a\),定义一个二元组 \((i,j)\) (其中 \(i<j\))会产生贡献,分为下面三种情况:
-
情况一:如果满足 \(\max_{k=i+1}^{j-1}a_k<\min(a_i,a_j)\),则会产生 \(p_1\) 的贡献。
-
情况二:如果满足 \(a_i<\max_{k=i+1}^{j-1}a_k<a_j\),则会产生 \(p_2\) 的贡献。
-
情况三:如果满足 \(a_j<\max_{k=i+1}^{j-1}a_k<a_i\),则会产生 \(p_2\) 的贡献。
进行 \(m\) 次询问,每次询问一个区间 \([l,r]\),问该区间中所有 \((i,j)\) 的贡献之和。
题解
知识点:树状数组,扫描线,单调栈。
没什么思维难度,只需要不断地推式子就行了。
-
情况一:如果满足 \(\max_{k=i+1}^{j-1}a_k<\min(a_i,a_j)\),则会产生 \(p_1\) 的贡献。
-
情况二:如果满足 \(a_i<\max_{k=i+1}^{j-1}a_k<a_j\),则会产生 \(p_2\) 的贡献。
-
情况三:如果满足 \(a_j<\max_{k=i+1}^{j-1}a_k<a_i\),则会产生 \(p_2\) 的贡献。
套路地,不考虑 \((i,j)\) 中有哪些 \(k\) 产生贡献,而是反过来看 \(k\) 能对哪些 \((i,j)\) 产生贡献。
单调栈预处理出 \(a_i\) 左边第一个比 \(a_i\) 大的数的下标 \(L_i\),如果没有则 \(L_i=0\),以及右边第一个比 \(a_i\) 大的数的下标 \(R_i\),如果没有则 \(R_i=n+1\)。
故当 \(a_k\) 作为 \((i+1,j-1)\) 区间最大值当且仅当 \(\begin{cases} i+1>L_k \\ j-1<R_k \end{cases}\),解得 \(\begin{cases} i\ge L_k \\ j\le R_k \end{cases}\)。
满足情况一时,必须有 \(\begin{cases} a_i>a_k \\ a_j<a_k \end{cases}\),可看作 \(\begin{cases} i\le L_k \\ j\ge R_k \end{cases}\),结合上式,此时只能 \(\begin{cases} i=L_k \\ j=R_k \end{cases}\)。
满足情况二时,必须有 \(\begin{cases} i\neq L_k\\ j\ge R_k \end{cases}\),推得 \(\begin{cases} i>L_k\\ j=R_k \end{cases}\)。
满足情况三时,同理可推得 \(\begin{cases} i=L_k\\ j<R_k \end{cases}\)。
那么对于一个询问 \([l,r]\),它的答案就有四个组成:
这时候果断对 \(r\) 按 \(1\sim n\) 扫描线,可以求出 \(ans_1\) 和 \(ans_2\),具体是把式子继续拆开,用树状数组维护即可。
\(ans_3\) 可以看作 \(ans_2\) 倒过来,那就倒过来对 \(l\) 扫描线。
\(ans_4\) 是相邻元素产生的贡献,由于上面的推理默认存在 \(k\),所以会遗漏这种 \(i,j\) 相邻的情况。
#include<bits/stdc++.h>
using namespace std;
#define rep(i,l,r) for(int i=(l);i<=(r);++i)
#define per(i,l,r) for(int i=(r);i>=(l);--i)
#define pr pair<int,int>
#define fi first
#define se second
#define pb push_back
#define all(x) (x).begin(),(x).end()
#define sz(x) (x).size()
#define bg(x) (x).begin()
#define ed(x) (x).end()
#define N 202506
#define int long long
int n,m,p1,p2,a[N],L[N],R[N];
int st[N],tp,ans[N];
vector<pr>e[N],g[N];
vector<int>rr[N],ri[N],ll[N],li[N];
inline void init(){
rep(i,1,n){
while(tp&&a[i]>a[st[tp]]){
R[st[tp]]=i;
tp--;
}
st[++tp]=i;
}
while(tp){
R[st[tp]]=n+1;
tp--;
}
per(i,1,n){
while(tp&&a[i]>a[st[tp]]){
L[st[tp]]=i;
tp--;
}
st[++tp]=i;
}
while(tp){
L[st[tp]]=0;
tp--;
}
}
struct BIT{
#define lb(x) (x&-x)
int tr[N];
inline void init(){
rep(i,0,n){
tr[i]=0;
}
}
inline void upd(int k,int d){
while(k<=n){
tr[k]+=d;
k+=lb(k);
}
}
inline int ask(int k){
int ans=0;
while(k){
ans+=tr[k];
k-=lb(k);
}
return ans;
}
#undef lb
}t1,t2,t3,ti;
signed main(){
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n>>m>>p1>>p2;
rep(i,1,n){
cin>>a[i];
}
init();
rep(i,1,n){
rr[R[i]].pb(L[i]);
ri[R[i]].pb(i);
ll[L[i]].pb(R[i]);
li[L[i]].pb(i);
}
rep(i,1,m){
int l,r;
cin>>l>>r;
g[l].pb({r,i});
e[r].pb({l,i});
ans[i]=(r-l)*p1;
}
rep(i,1,n){
for(int l:rr[i]){
if(l){
t1.upd(l,1);
}
t2.upd(l+1,1);
t3.upd(l+1,l+1);
}
for(int x:ri[i]){
ti.upd(x,x);
t2.upd(x+1,-1);
}
for(pr u:e[i]){
ans[u.se]+=(t1.ask(n)-t1.ask(u.fi-1))*p1;
ans[u.se]+=(ti.ask(n)-ti.ask(u.fi-1))*p2;
ans[u.se]-=t2.ask(u.fi)*u.fi*p2;
ans[u.se]-=(t3.ask(n)-t3.ask(u.fi))*p2;
}
}
t1.init();
t2.init();
t3.init();
ti.init();
per(i,1,n){
for(int r:ll[i]){
t2.upd(r-1,1);
t3.upd(r-1,r-1);
}
for(int x:li[i]){
if(x-1){
t2.upd(x-1,-1);
}
ti.upd(x,x);
}
for(pr u:g[i]){
ans[u.se]-=ti.ask(u.fi)*p2;
ans[u.se]+=(t2.ask(n)-t2.ask(u.fi-1))*u.fi*p2;
ans[u.se]+=t3.ask(u.fi-1)*p2;
}
}
rep(i,1,m){
cout<<ans[i]<<"\n";
}
return 0;
}