Luogu3722 影魔

真的比较神的一个题

Description

link

给定一个排列,如果点对 ((i,j)) 满足:

(1.) (i ightarrow j) 中(两端不含)元素均小于 (p_i)(p_j) 则产生 (a_1) 点贡献

(2.) (i ightarrow j)中(两端不含)元素的最大值如果在(p_i)(p_j) 之间则产生 (a_2) 点贡献

Solution

首先发现要贡献法……想不到就没救啦

分开考虑两种值

第一种我们枚举一个点当做区间最大值,找到左右离得最近的比它大的两个数,这里就产生了贡献

然后第二种

对于每个点(i)

(l ightarrow i)(r)

(i ightarrow r)(l)

分别是满足条件的

然后我们看怎么求

左右的两个大数是可以单调栈求的

for(int i=1;i<=n+1;++i) 
{
	while(top&&a[st[top]]<a[i]) r[st[top]]=i,top--;
	l[i]=st[top]; st[++top]=i;
}

然后我们统计答案(蒟蒻不会这里的统计答案)

其实就是个区间加区间求和的线段树,不知道为啥没想到

还搞了(std) 才看懂……

Code

#include<bits/stdc++.h>
using namespace std;
#define int long long
namespace yspm{
	inline int read()
	{
		int res=0,f=1; char k;
		while(!isdigit(k=getchar())) if(k=='-') f=-1;
		while(isdigit(k)) res=res*10+k-'0',k=getchar();
		return res*f;
	}
	const int N=2e5+10;
	struct tree{
		int l,r,sum,add;
		#define l(p) t[p].l
		#define r(p) t[p].r
		#define sum(p) t[p].sum
		#define add(p) t[p].add
	}t[N<<5];
	int rt[N],tot,l[N],r[N],st[N],top,n,m,p1,p2,a[N];
	inline void build(int p,int l,int r)
	{
		l(p)=l; r(p)=r; if(l==r) return ;
		int mid=(l+r)>>1; build(p<<1,l,mid); build(p<<1|1,mid+1,r);
		return ;
	}
	inline void spread(int p)
	{
		if(add(p))
		{
			sum(p<<1)+=add(p)*(r(p<<1)-l(p<<1)+1);
			sum(p<<1|1)+=add(p)*(r(p<<1|1)-l(p<<1|1)+1);
			add(p<<1)+=add(p); add(p<<1|1)+=add(p);
		}return add(p)=0,void();
	}
	inline void push_up(int p){return sum(p)=sum(p<<1)+sum(p<<1|1),void();}
	inline void update(int p,int l,int r,int v)
	{
		if(l<=l(p)&&r(p)<=r) return sum(p)+=v*(r(p)-l(p)+1),add(p)+=v,void();
		int mid=(l(p)+r(p))>>1; spread(p);
		if(l<=mid) update(p<<1,l,r,v);
		if(r>mid) update(p<<1|1,l,r,v); 
		return push_up(p);
	}
	inline int ask(int p,int l,int r)
	{
		if(l<=l(p)&&r(p)<=r) return sum(p);
		spread(p); int ans=0,mid=(l(p)+r(p))>>1;
		if(l<=mid) ans+=ask(p<<1,l,r); if(r>mid) ans+=ask(p<<1|1,l,r);
		return ans;
	}
	int ans[N];
	struct node{
		int l,r,id,val;
	}; vector<node> vec1[N],vec2[N];
	signed main()
	{
		n=read(); m=read(); p1=read(); p2=read();
		for(int i=1;i<=n;++i) a[i]=read(); a[0]=a[n+1]=n+1;
		for(int i=1;i<=n+1;++i) 
		{
			while(top&&a[st[top]]<a[i]) r[st[top]]=i,top--;
			l[i]=st[top]; st[++top]=i;
		}
		for(int i=1;i<=m;++i) 
		{
			int tl=read(),tr=read();
			ans[i]+=(tr-tl)*p1;
			vec2[tl-1].push_back((node){tl,tr,i,0});
			vec2[tr].push_back((node){tl,tr,i,0});
		}
		for(int i=1;i<=n;++i)
		{
			if(l[i]&&r[i]<=n) vec1[r[i]].push_back((node){l[i],l[i],0,p1});
			if(l[i]&&r[i]>i+1) vec1[l[i]].push_back((node){i+1,r[i]-1,0,p2});
			if(l[i]+1<i&&r[i]<=n) vec1[r[i]].push_back((node){l[i]+1,i-1,0,p2});
		}
		
		build(1,1,n);
		for(int i=1;i<=n;++i)
		{
			int siz=vec1[i].size();
			for(int j=0;j<siz;++j) 
			{
				update(1,vec1[i][j].l,vec1[i][j].r,vec1[i][j].val);
			}
			siz=vec2[i].size();
			for(int j=0;j<siz;++j) 
			{
				int sum=ask(1,vec2[i][j].l,vec2[i][j].r);
				if(i==vec2[i][j].r) ans[vec2[i][j].id]+=sum; 
				else ans[vec2[i][j].id]-=sum; 
			}
		}
		for(int i=1;i<=m;++i) printf("%lld
",ans[i]);
		return 0;
	}
}
signed main(){return yspm::main();}

Review

(1.) 贡献法好!

(2.) 提升代码能力和学习如何统计答案……

原文地址:https://www.cnblogs.com/yspm/p/12724839.html