[AH2017/HNOI2017]影魔 题解
[AH2017/HNOI2017]影魔 题解
题意
\(~~~~\) 给出长为 \(n\) 的排列 \(\{k\}\) ,共 \(m\) 次给出 \([a,b]\) ,求:
\[\sum_{i=a}^b\sum_{j=i+1}^b [k_i>k_s \land k_j>k_s]\times p_1 +[k_i<k_s<k_j \lor k_i>k_s>s_j]\times p_2 (k_s=\max_{l=i+1}^{j-1} \{k_l\}) \]
题解
本文版权归Azazel与博客园共有,欢迎转载,但需保留此声明,并给出原文地址,谢谢合作。
原文地址:https://www.cnblogs.com/Azazel/p/14964757.html
\(~~~~\) 考虑处理出每个位置 \(i\) 左边第一个比它大的数的位置 \(L_i\) 和右边第一个比它大的数的位置 \(R_i\) 。则考虑某个位置上的数作为中间最大值(即题意中 \(k_s\)) 时可以使得哪些区间产生贡献。
\(~~~~\) 对于 \(p_1\) 类的贡献,则此时仅有区间 \([L_i,R_i]\) 会以 \(i\) 作为 \(S\) 产生 \(p_1\) 贡献。当区间继续扩大时最大值不是 \(k_i\) ,当区间缩小时无法满足 \(k_l>k_s\) ,故仅有该区间满足条件。
\(~~~~\) 对于 \(p_2\) 类的贡献,则所有满足 \(l\in[L_i+1,i-1],r=R_i\) 或 \(l=L_i,r\in[i+1,R]\) 的区间 \([l,r]\) 均会产生 \(p_2\) 的贡献,区间不漏的原因同上。
\(~~~~\) 因此,现在从左往右扫一遍 \(n\) ,对于 \(p_1\) 类的贡献,当扫到一个 \(R_i\) 时,将其对应的 \(L_i\) 贡献加上 \(p_1\) ,并将 \([L_i+1,i-1]\) 的贡献加上 \(p_2\) ,扫到一个 \(L_i\) 时,将 \([i+1,R_i]\) 的贡献加上 \(p_2\) 。并将所有询问拆成两个,在 \(l-1\) 处时得到 \([l,r]\) 的总贡献为 \(sum_1\) ,在 \(r\) 处时再得到 \([l,r]\) 的总贡献为 \(sum_2\) ,则显然区间 \([l,r]\) 本身的答案为 \(sum_2-sum_1\) 。
代码
查看代码
#include <cstdio>
#include <vector>
#include <algorithm>
#define ll long long
using namespace std;
int n,m,p1,p2;
int sta[200005],top,arr[200005];
int L[200005],R[200005];
int ls[200005],rs[200005];
ll Ans[200005];
struct node{
int id,l,r;
node(){}
node(int Id,int L,int R){id=Id,l=L,r=R;}
};
struct Range{
int l,r,val;
Range(){}
Range(int L,int R,int Val){l=L,r=R,val=Val;}
};
vector <Range> op[200005];
vector <node> Post[200005],Nega[200005];
void dfs(int u)
{
if(ls[u])
{
L[ls[u]]=L[u];
R[ls[u]]=u-1;
dfs(ls[u]);
}
if(rs[u])
{
L[rs[u]]=u+1;
R[rs[u]]=R[u];
dfs(rs[u]);
}
}
void Pre()
{
for(int i=1;i<=n;i++)
{
int k=top;
while(k&&arr[sta[k]]<arr[i]) k--;
if(k) rs[sta[k]]=i;
if(k<top) ls[i]=sta[k+1];
sta[++k]=i;top=k;
}
L[sta[1]]=1,R[sta[1]]=n;
dfs(sta[1]);
}
struct SegmentTree{
#define ls p<<1
#define rs p<<1|1
#define lson p<<1,l,mid
#define rson p<<1|1,mid+1,r
ll tr[800005],tag[800005];
void pushUp(int p){tr[p]=tr[ls]+tr[rs];}
void pushDown(int p,int l,int r)
{
if(!tag[p]) return;
int mid=(l+r)>>1;
tr[ls]+=tag[p]*(mid-l+1);
tr[rs]+=tag[p]*(r-mid);
tag[ls]+=tag[p];tag[rs]+=tag[p];
tag[p]=0;
return;
}
void Modify(int p,int l,int r,int lx,int rx,int val)
{
if(rx<lx) return;
if(lx<=l&&r<=rx)
{
tag[p]+=val;
tr[p]+=1ll*(r-l+1)*val;
return;
}
pushDown(p,l,r);
int mid=(l+r)>>1;
if(lx<=mid) Modify(lson,lx,rx,val);
if(mid<rx) Modify(rson,lx,rx,val);
pushUp(p);
}
ll Query(int p,int l,int r,int lx,int rx)
{
if(rx<lx) return 0;
if(lx<=l&&r<=rx) return tr[p];
int mid=(l+r)>>1;ll ret=0;
pushDown(p,l,r);
if(lx<=mid) ret+=Query(lson,lx,rx);
if(mid<rx) ret+=Query(rson,lx,rx);
return ret;
}
}Seg;
template<typename T>void read(T &x)
{
T f=1;x=0;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9') {x=x*10+s-'0';s=getchar();}
x*=f;
}
int main() {
read(n);read(m);read(p1);read(p2);
arr[0]=arr[n+1]=n+1;
for(int i=1;i<=n;i++) read(arr[i]);
Pre();
for(int i=1;i<=n;i++)
{
L[i]--,R[i]++;
if(1<=L[i]&&R[i]<=n) op[R[i]].push_back(Range(L[i],L[i],p1));
if(L[i]+1<i&&R[i]<=n) op[R[i]].push_back(Range(L[i]+1,i-1,p2));
if(1<=L[i]&&R[i]>i+1) op[L[i]].push_back(Range(i+1,R[i]-1,p2));
}
for(int i=1,l,r;i<=m;i++)
{
read(l);read(r);Ans[i]+=(r-l)*p1;
Nega[l-1].push_back(node(i,l,r));
Post[r].push_back(node(i,l,r));
}
for(int i=0;i<=n+1;i++)
{
for(int j=0;j<op[i].size();j++)
{
Range Op=op[i][j];
Seg.Modify(1,0,n+1,Op.l,Op.r,Op.val);
}
for(int j=0;j<Nega[i].size();j++)
{
node Op=Nega[i][j];
Ans[Op.id]-=Seg.Query(1,0,n+1,Op.l,Op.r);
}
for(int j=0;j<Post[i].size();j++)
{
node Op=Post[i][j];
Ans[Op.id]+=Seg.Query(1,0,n+1,Op.l,Op.r);
}
}
for(int i=1;i<=m;i++) printf("%lld\n",Ans[i]);
return 0;
}
```
</details>