1. 程式人生 > 實用技巧 >[AH2017/HNOI2017]影魔

[AH2017/HNOI2017]影魔

題目

[AH2017/HNOI2017]影魔

做法

做法1(口胡)

這個做法估計會T

考慮每個數字連向後面第一個比他大的樹,對一個區間建一顆樹,對於\(i<j<k,a_i<a_j<a_k\)的詢問,即為樹上每個點\(dep-2\)之和,然後其餘類似,用莫隊維護,時間複雜度:\(O(n\sqrt{n})\),常數和碼量都巨大,於是沒有打,膜了題解。

當然,至於為什麼為\(dep-2\)之和,因為我們預設\(i<j<k,a_i<a_j<a_k\)是由\((i,k)\)區間中最大的數字來找到這個區間。

做法2

以下摘自此部落格

這個題可以採取離線處理的方式.先處理出每個點i左邊第一個比它大的點L[i],和右邊第一個比它大的點R[i].

那麼對於區間L[i]到R[i]有p1的貢獻.①

對於左端點在L[i]+1到i-1,右端點為R[i]的區間有p2的貢獻.②

對於左端點為L[i],右端點為i+1到R[i]-1的區間也有p2的貢獻.③

所以我們離線排序處理好.

對於①情況,我們在掃到R[i]時,更新點L[i]的貢獻

對於②情況,我們在掃到R[i]時,更新區間L[i]+1到i-1的貢獻

對於③情況,我們在掃到L[i]時,更新區間i+1到R[i]-1的貢獻

我們對於每個詢問[l,r],在掃到l-1時,我們記錄此時區間l到r的每個點的貢獻和為sum1,然後當我們掃到r的時候,再次記錄此時的區間l到r的每個點的貢獻和為sum2,顯然答案就是sum2-sum1了.

好,至於為什麼嗎,我來解釋一下,為什麼\(p1\)的貢獻只用\(L[i],R[i]\)來統計呢?(當然,這也可以證明\(p1\)區間個數實在\(O(n)\)級別的)

  1. 為什麼每個區間必定會被統計且只會被統計一次:首先,考慮這個\((l,r)\)是個合法的\(p1\)區間,那麼對於\((l,r)\)範圍中最大的數字\(mid\),其\(L[i]=l,R[i]=r\),且對於這個區間其他數字,絕對不可能\(L[i]=l,R[i]=r\),因為\(mid\)比他們都打,卡在中間擋住了他們。
  2. 為什麼\((L[i],R[i])\)一定構成一個合法區間:這不廢話?

對於\(p2\)\(a_i<a_j<a_k\)

,證明方法類似,每個區間由這個區間中最大的數字來統計,\(a_{i}>a_{j}>a_{k}\)

當然,別忘了\((i,i+1)\)固定有\(p1\)的貢獻。

時間複雜度:\(O(nlogn)\)

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define  N  210000
#define  NN  410000
#define  NNN  610000
using  namespace  std;
typedef  long  long  LL;
struct  node
{
	int  l,r,d;
	LL  lazy,c;
}tr[NN];int  len,last[N];
inline  void  pushlazy(int  x,LL  k){tr[x].lazy+=k;tr[x].c+=k*tr[x].d;}
inline  void  updata(int  x){tr[x].c=tr[tr[x].l].c+tr[tr[x].r].c;}
inline  void  downdata(int  x)
{
	if(tr[x].lazy)
	{
		pushlazy(tr[x].l,tr[x].lazy);
		pushlazy(tr[x].r,tr[x].lazy);
		tr[x].lazy=0;
	}
}
void  bt(int  l,int  r)
{
	int  now=++len;tr[now].d=(r-l+1);
	if(l<r)
	{
		int  mid=(l+r)>>1;
		tr[now].l=len+1;bt(l,mid);
		tr[now].r=len+1;bt(mid+1,r);
	}
}
void  change(int  now,int  l,int  r,int  ll,int  rr,LL  k)
{
	if(l==ll  &&  r==rr){pushlazy(now,k);return  ;}
	int  mid=(l+r)>>1;
	downdata(now);
	if(rr<=mid)change(tr[now].l,l,mid,ll,rr,k);
	else  if(mid<ll)change(tr[now].r,mid+1,r,ll,rr,k);
	else  change(tr[now].l,l,mid,ll,mid,k),change(tr[now].r,mid+1,r,mid+1,rr,k);
	updata(now);
}
LL  findans(int  now,int  l,int  r,int  ll,int  rr)
{
	if(l==ll  &&  r==rr)return  tr[now].c;
	int  mid=(l+r)>>1;
	downdata(now);
	if(rr<=mid)return  findans(tr[now].l,l,mid,ll,rr);
	else  if(mid<ll)return  findans(tr[now].r,mid+1,r,ll,rr);
	else  return  findans(tr[now].l,l,mid,ll,mid)+findans(tr[now].r,mid+1,r,mid+1,rr);	
}
int  L[N],R[N],n,m,a[N];
LL  q1,q2;
int  sta[N],top;
struct  Query
{
	int  l,r;
	LL  *id,type;
};
struct  CHANGE
{
	int  l,r,k,id/*在訪問到哪個人時會使用這個*/;
}ch[NNN];int  clen,cnow=1;
inline  bool  cmp(CHANGE  x,CHANGE  y){return  x.id<y.id;}
vector<Query> fuck[N];
LL  ans[N];
inline  void  solve(int  x/*加入第x個位置*/)
{
	while(cnow<=clen  &&  ch[cnow].id==x)
	{
		if(ch[cnow].l<=ch[cnow].r)change(1,1,n,ch[cnow].l,ch[cnow].r,ch[cnow].k);
		cnow++;
	}
	for(int  i=0;i<fuck[x].size();i++)
	{
		Query  y=fuck[x][i];
		(*y.id)+=y.type*findans(1,1,n,y.l,y.r);
	}
}
int  main()
{
	scanf("%d%d%d%d",&n,&m,&q1,&q2);
	for(int  i=1;i<=n;i++)scanf("%d",&a[i]);
	for(int  i=1;i<=n;i++)
	{
		while(top  &&  a[sta[top]]<a[i])top--;
		L[i]=sta[top];
		sta[++top]=i;
	}
	top=0;
	for(int  i=n;i>=1;i--)
	{
		while(top  &&  a[sta[top]]<a[i])top--;
		R[i]=sta[top];
		sta[++top]=i;
	}
	for(int  i=1;i<=n;i++)
	{
		if(L[i]  &&  R[i])
		{
			ch[++clen].id=R[i];ch[clen].k=q1;ch[clen].l=L[i];ch[clen].r=L[i];
		}
		if(R[i])
		{
			ch[++clen].id=R[i];ch[clen].k=q2;ch[clen].l=L[i]+1;ch[clen].r=i-1;
		}
		if(L[i])
		{
			ch[++clen].id=L[i];ch[clen].k=q2;ch[clen].l=i+1;ch[clen].r=!R[i]?n:R[i]-1;
		}
	}
	bt(1,n);
	sort(ch+1,ch+clen+1,cmp);
	for(int  i=1;i<=m;i++)
	{
		int  l,r;scanf("%d%d",&l,&r);
		ans[i]=(r-l)*q1;
		Query  x;x.l=l;x.r=r;x.type=-1;x.id=&ans[i];
		fuck[l-1].push_back(x);
		x.type=1;x.id=&ans[i];
		fuck[r].push_back(x);
	}
	for(int  i=1;i<=n;i++)
	{
		solve(i);
	}
	for(int  i=1;i<=m;i++)printf("%lld\n",ans[i]);
	return  0;
}