1. 程式人生 > 其它 >luogu P3722 [AH2017/HNOI2017]影魔

luogu P3722 [AH2017/HNOI2017]影魔

題面傳送門
線段樹是什麼勾八玩意兒。
看到題一頭霧水,然後看了看部分分,有個奇怪的\(p1=2p2\)
略微撕烤一下這檔分怎麼做,大概就是第一種貢獻可以看作兩個第二個貢獻的疊加。
於是我們可以把題意轉化一下:
第一種貢獻:如果一個區間\([l,r]\),滿足\(a_r\)大於\([l+1,r-1]\)最大值,那麼貢獻\(p2\),如果\(a_l\)也是這樣,疊加貢獻\(p2\)
第二種貢獻:如果一個區間\([l,r]\),滿足\(a_r,a_l\)均大於區間中的數,那麼貢獻\(p1-2p2\)
第一種貢獻顯然是可算的,直接單調棧然後掃描線,第二種貢獻看上去就不是那麼可做。
證明一個結論:造成第二種貢獻的區間個數是\(O(n)\)

的。
考慮掃描線維護單調棧的過程,如果\(a_i\)與前面的一個\(a_l\)構成了上面這種區間,要麼\(a_l>a_i\),這對每個數來說停止彈棧,是\(O(n)\)的,第二種是\(a_l<a_i\),那麼接下來\(a_l\)被彈出,也是\(O(n)\)的,所以這個貢獻在掃描線的時候暴力修改就好了。
時間複雜度\(O(n\log n)\),寫了個樹狀陣列才跑起來像個log樣。
code:


#include<bits/stdc++.h>
#define I inline
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define abs(x) ((x)>0?(x):-(x))
#define ll long long
#define db double
#define lb long db
#define N (200000+5)
#define M ((N<<2)+5)
#define Ks (12+5)
#define mod 1000000007
#define Mod (mod-1)
#define eps (1e-9)
#define ull unsigned ll
#define it iterator
#define Gc() getchar() 
#define Me(x,y) memset(x,y,sizeof(x))
#define Mc(x,y) memcpy(x,y,sizeof(x))
#define d(x,y) (n*(x-1)+(y))
#define R(n) (1ll*rand()*rand()%(n)+1)
#define Pc(x) putchar(x)
#define LB lower_bound
#define UB upper_bound
#define PB push_back
using namespace std;struct Ques{int w,id;};vector<Ques> S[N];
int n,m,k,L[N],R[N],X[N],Y[N],z,p1,p2,st[N],H,A[N];ll Ans[N];
namespace Tree{
	ll F1[N],F2[N];I void I1(int x,int y){while(x<=n) F1[x]+=y,x+=x&-x;}I void I2(int x,int y){while(x<=n) F2[x]+=y,x+=x&-x;}
	I ll Q1(int x){ll Ans=0;while(x) Ans+=F1[x],x-=x&-x;return Ans;}I ll Q2(int x){ll Ans=0;while(x) Ans+=F2[x],x-=x&-x;return Ans;}
	I void Ins(int x,int y,int z){I1(x,z);I2(x,z*(x-1));I1(y+1,-z);I2(y+1,-z*y);}
	I ll Qry(int x,int y){return Q1(y)*y-Q2(y)-Q1(x-1)*(x-1)+Q2(x-1);}
}
int main(){
	freopen("1.in","r",stdin);
	int i,j;scanf("%d%d%d%d",&n,&m,&p1,&p2);for(i=1;i<=n;i++) scanf("%d",&A[i]),L[i]=0,R[i]=n+1;H=0;for(i=1;i<=n;i++) {while(H&&A[st[H]]<A[i]) R[st[H--]]=i;L[i]=st[H];st[++H]=i;}
	for(i=1;i<=m;i++) scanf("%d%d",&X[i],&Y[i]),S[Y[i]].PB((Ques){X[i],i});H=0;for(i=1;i<=n;i++){
		Tree::Ins(max(L[i],1),i-1,p2);while(H&&A[st[H]]<A[i]) Tree::Ins(st[H],st[H],p1-2*p2),H--;H&&(Tree::Ins(st[H],st[H],p1-2*p2),0);st[++H]=i;for(Ques d:S[i]) Ans[d.id]=Tree::Qry(d.w,i);
	}for(i=1;i<=n;i++) S[i].clear();for(i=1;i<=m;i++) S[X[i]].PB((Ques){Y[i],i});Me(Tree::F1,0);Me(Tree::F2,0);
	for(i=n;i;i--){Tree::Ins(i+1,min(n,R[i]),p2);for(Ques d:S[i]) Ans[d.id]+=Tree::Qry(i,d.w);} for(i=1;i<=m;i++) printf("%lld\n",Ans[i]);
}