[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>
上一篇:TestOS移植K210开发板(上)


下一篇:BZOJ4545 DQS的trie