[AH2017/HNOI2017] 影魔

[AH2017/HNOI2017]影魔

30pts

暴力在每个区间中枚举点对 ( a , b ) (a,b) (a,b)并查询点对之间的最大值 m a x max max,若 m a x &lt; = m i n ( a , b ) max&lt;=min(a,b) max<=min(a,b)则贡献 p 1 p1 p1,若 m i n ( a , b ) &lt; m a x &lt; m a x ( a , b ) min(a,b)&lt;max&lt;max(a,b) min(a,b)<max<max(a,b)则贡 p 2 p2 p2

60pts的tip

这个 p 1 = 2 ∗ p 2 p1=2*p2 p1=2p2提醒我们什么事情?当我们查询到 m a x max max时,我们做了一个判断,若 a , b a,b a,b中有两个数大于 m a x max max则贡献 p 1 p1 p1,若只有一个数大于 m a x max max就贡献 p 2 p2 p2,所以我们发现这两种情况其实是有关联的。于是我们又发现了一件事情,我们对于每一对 ( a , b ) (a,b) (a,b),只关心其中的 m a x max max,并不关心其他数,所以我们考虑枚举中间那个 m a x max max,然后找到 m a x max max左/右边比他大的第一个位置 l [ i ] / r [ i ] l[i]/r[i] l[i]/r[i],那么就有以下三种情况:
1,区间 [ l [ i ] , r [ i ] − 1 ] , [ l [ i ] , r [ i ] − 2 ] , . . . , [ l [ i ] , i + 1 ] [l[i],r[i]-1],[l[i],r[i]-2],...,[l[i],i+1] [l[i],r[i]1],[l[i],r[i]2],...,[l[i],i+1]这些区间都能贡献 p 2 p2 p2
2,区间 [ l [ i ] + 1 , r [ i ] ] , [ l [ i ] + 2 , r [ i ] ] , . . . , [ i − 1 , r [ i ] ] [l[i]+1,r[i]],[l[i]+2,r[i]],...,[i-1,r[i]] [l[i]+1,r[i]],[l[i]+2,r[i]],...,[i1,r[i]]这些区间都能贡献 p 2 p2 p2
3,区间 [ l [ i ] , r [ i ] ] [l[i],r[i]] [l[i],r[i]]可以贡献一个 p 1 p1 p1

其实到这里只剩复杂度优化的问题了

100pts

离线处理,对于上述三种情况有以下:
1,这一类区间左端点固定,将询问按照左端点排序,从右往左扫,如果遇到一个 l [ i ] l[i] l[i]就在线段树上把区间 [ i + 1 , r [ i ] − 1 ] [i+1,r[i]-1] [i+1,r[i]1]全部加上 p 2 p2 p2,如果遇到一个询问的左端点L,就在线段树上查询 [ L , R ] [L,R] [L,R]的和
2,几乎同上,反着做一遍
3,两边扫都可以,在线段树上可以包含贡献区间的位置区间加上,扫的过程中单点查询即可

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e6+5;
int n,m,a[N],l[N],r[N];
ll t[N<<2],tag[N<<2],p1,p2;
stack <int> Q;
struct query
{
	int l,r,id;
	ll ans;
}q[N];
struct data
{
	int pos,l,r;
}f[N];
bool cmpa(const data x,const data y)
{
	return x.l<y.l;
}
bool cmpb(const data x,const data y)
{
	return x.r<y.r;
}
bool cmp1(const query x,const query y)
{
	return x.l<y.l;
}
bool cmp2(const query x,const query y)
{
	return x.r<y.r;
}

bool cmp3(const query x,const query y)
{
	return x.id<y.id;
}
int read()
{
	int x=0,f=1;
	char c=getchar();
	while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
	while (c>='0'&&c<='9') {x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return f*x;
}
int Read()
{
	ll x=0,f=1;
	char c=getchar();
	while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
	while (c>='0'&&c<='9') {x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return f*x;
}
void Get_lr()
{
	a[0]=a[n+1]=n+1;
	while (Q.size()) Q.pop();
	Q.push(0);
	for (int i=1;i<=n;i++)
	{
		while (Q.size()&&a[i]>a[Q.top()]) Q.pop();
		l[i]=Q.top();
		Q.push(i);
	}
	while (Q.size()) Q.pop();
	Q.push(n+1);
	for (int i=n;i>=1;i--)
	{
		while (Q.size()&&a[i]>a[Q.top()]) Q.pop();
		r[i]=Q.top();
		Q.push(i);
	}
	for (int i=1;i<=n;i++) f[i]=(data){i,l[i],r[i]};
	return;
}
void Pushdown(int p,int ls,int rs,int l,int r)
{
	if (!tag[p]) return;
	int mid=(l+r)>>1;
	tag[ls]+=tag[p];
	tag[rs]+=tag[p];
	t[ls]+=tag[p]*(mid-l+1);
	t[rs]+=tag[p]*(r-(mid+1)+1);
	tag[p]=0;
	return;
}
void Modify(int p,int l,int r,int L,int R,ll v)
{
	if (L<=l&&r<=R)
	{
		t[p]+=v*(r-l+1);
		tag[p]+=v;
		return;
	}
	int ls=p<<1,rs=p<<1|1,mid=(l+r)>>1;
	Pushdown(p,ls,rs,l,r);
	if (L<=mid) Modify(ls,l,mid,L,R,v);
	if (R>mid) Modify(rs,mid+1,r,L,R,v);
	t[p]=t[ls]+t[rs];
	return;
}
ll Query(int p,int l,int r,int L,int R)
{
	if (L<=l&&r<=R) return t[p];
	ll ret=0;
	int ls=p<<1,rs=p<<1|1,mid=(l+r)>>1;
	Pushdown(p,ls,rs,l,r);
	if (L<=mid) ret+=Query(ls,l,mid,L,R);
	if (R>mid) ret+=Query(rs,mid+1,r,L,R);
	t[p]=t[ls]+t[rs];
	return ret;
}
int Find1(int val)
{
	int l=1,r=n,mid;
	while (l<r)
	{
		mid=(l+r)>>1;
		if (f[mid].l>=val) r=mid;
		else l=mid+1;
	}
	return l;
}
int Find2(int val)
{
	int l=1,r=n,mid;
	while (l<r)
	{
		mid=(l+r+1)>>1;
		if (f[mid].r<=val) l=mid;
		else r=mid-1;
	}
	return l;
}
void Solve()
{
	//case 1
	memset(t,0,sizeof(t));
	memset(tag,0,sizeof(tag));
	sort(q+1,q+m+1,cmp1);
	sort(f+1,f+n+1,cmpa);
	int now=n+1;
	for (int i=m;i>=1;i--)
	{
		int tmp;
		tmp=Find1(q[i].l);
		for (int j=now-1;j>=tmp;j--) 
			if (f[j].pos+1<=f[j].r-1) Modify(1,0,n+1,f[j].pos+1,f[j].r-1,p2);
		now=tmp;
		q[i].ans+=Query(1,0,n+1,q[i].l,q[i].r);
	}
	//case 2
	memset(t,0,sizeof(t));
	memset(tag,0,sizeof(tag));
	sort(q+1,q+m+1,cmp2);
	sort(f+1,f+n+1,cmpb);
	now=0;
	for (int i=1;i<=m;i++)
	{
		int tmp;
		tmp=Find2(q[i].r);
		for (int j=now+1;j<=tmp;j++) 
			if (f[j].l+1<=f[j].pos-1) Modify(1,0,n+1,f[j].l+1,f[j].pos-1,p2);
		now=tmp;
		q[i].ans+=Query(1,0,n+1,q[i].l,q[i].r);
	}
	//case 3
	memset(t,0,sizeof(t));
	memset(tag,0,sizeof(tag));
	sort(q+1,q+m+1,cmp2);
	sort(f+1,f+n+1,cmpb);
	now=0;
	for (int i=1;i<=m;i++)
	{
		int tmp;
		tmp=Find2(q[i].r);
		for (int j=now+1;j<=tmp;j++)
			if (1<=f[j].l) Modify(1,0,n+1,1,f[j].l,p1);
		now=tmp;
		q[i].ans+=Query(1,0,n+1,q[i].l,q[i].l);
	}
	return;
}
int main()
{
	n=read(),m=read(),p1=Read(),p2=Read();
	for (int i=1;i<=n;i++) a[i]=read();
	for (int i=1;i<=m;i++) q[i].l=read(),q[i].r=read(),q[i].id=i;
	Get_lr();
	Solve();
	sort(q+1,q+m+1,cmp3);
	for (int i=1;i<=m;i++) q[i].ans+=(q[i].r-q[i].l)*p1,printf("%lld\n",q[i].ans);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值