*题解: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;
}

浙公网安备 33010602011771号