[BZOJ4826][HNOI2017]影魔 可持久化線段樹
阿新 • • 發佈:2018-11-07
題意:給你 \(1\) 到 \(n\) 的排列 \(k_1,k_2,\dots,k_n\) ,對 \(i,j (i<j)\)來說,若不存在 \(k_s (i<s<j)\) 大於 \(k_i\) 或者 \(k_j\),則會產生 \(p_1\) 的貢獻。另一種情況,令 \(c\) 為 \(k_{i + 1}, k_{i + 2}, \cdots, k_{j -1}\) 的最大值,若 \(c\) 滿足:\(k_i < c < k_j\),或者 \(k_j < c < k_i\),則會為產生 \(p_2\) 的貢獻 ,多次詢問區間 \([l_i,r_i]\)
的貢獻值
題解
第一種情況,\(k_i\) 和 \(k_j\) 是 \([i,j]\) 的最大值和次大值,第二種情況,是最大值和第三大值
對於每個 \(i\) ,用單調棧預處理出它左邊第一個比他大的位置 \(lpos[i]\) ,右邊第一個比他大的位置 \(rpos[i]\) ,那麼第一種情況是所有的 \(lpos[i],rpos[i]\) 和 \(i,i+1\) (前者並不會算重複),第二種情況是所有的 \(lpos[i],i+1\dots rpos[i]-1\) 和 \(lpos[i]+1\dots i-1,rpos[i]\)
由於是詢問區間 \([l,r]\) 的值,而且發現上述所有的點對,某一點都是定的,因此開一棵可持久化線段樹搞一下這個(相當於查詢二維座標系下 \((l,l)\)
好像有掃描線的做法,不過我不會掃描線,先留個坑以後再補(咕咕咕)
#include<bits/stdc++.h> #define REP(i,a,b) for(int i(a);i<=(b);++i) #define dbg(...) fprintf(stderr,__VA_ARGS__) const int S=1<<18; char ibuf[S],*iS,*iT; #define gc (iS==iT?iT=ibuf+fread(iS=ibuf,1,S,stdin),iS==iT?EOF:*iS++:*iS++) inline int read(){char c,p=0;int w; while(isspace(c=gc));if(c=='-')p=1,c=gc;w=c&15; while(isdigit(c=gc))w=w*10+(c&15);return p?-w:w; } template<typename T,typename U>inline bool smin(T&x,const U&y){return x>y?x=y,1:0;} template<typename T,typename U>inline bool smax(T&x,const U&y){return x<y?x=y,1:0;} typedef long long ll; const int N=2e5+5; struct node{ int ls,rs,w;ll sum; }t[N*70]; int n,m,p1,p2,cnt,rt[N],a[N],s[N],top,lpos[N],rpos[N]; inline void ins(int&o,int l,int r,int x,int y,int z){ t[++cnt]=t[o];t[o=cnt].sum+=1ll*(y-x+1)*z; if(x<=l&&r<=y){t[o].w+=z;return;} int mid=l+r>>1; if(y<=mid)ins(t[o].ls,l,mid,x,y,z); else if(x>mid)ins(t[o].rs,mid+1,r,x,y,z); else ins(t[o].ls,l,mid,x,mid,z),ins(t[o].rs,mid+1,r,mid+1,y,z); } inline ll ask(int x,int y,int l,int r,int ql,int qr){ if(ql<=l&&r<=qr)return t[y].sum-t[x].sum; int mid=l+r>>1;ll ans=1ll*(qr-ql+1)*(t[y].w-t[x].w); if(qr<=mid)return ans+ask(t[x].ls,t[y].ls,l,mid,ql,qr); if(ql>mid)return ans+ask(t[x].rs,t[y].rs,mid+1,r,ql,qr); return ans+ask(t[x].ls,t[y].ls,l,mid,ql,mid)+ask(t[x].rs,t[y].rs,mid+1,r,mid+1,qr); } struct data{int l,r,w;}; std::vector<data>g[N]; #define pb push_back int main(){ n=read(),m=read(),p1=read(),p2=read(); REP(i,1,n)a[i]=read(); s[top=1]=0;a[0]=a[n+1]=1e9; REP(i,1,n){ while(top&&a[i]>a[s[top]])--top; lpos[i]=s[top];s[++top]=i; } s[top=1]=n+1; for(int i=n;i;--i){ while(top&&a[i]>a[s[top]])--top; rpos[i]=s[top];s[++top]=i; } REP(i,1,n){ if(i<n)g[i].pb((data){i+1,i+1,p1}); if(lpos[i]&&rpos[i]<=n)g[lpos[i]].pb((data){rpos[i],rpos[i],p1}); if(lpos[i]&&i<rpos[i]-1)g[lpos[i]].pb((data){i+1,rpos[i]-1,p2}); if(rpos[i]<=n&&i>lpos[i]+1)g[rpos[i]].pb((data){lpos[i]+1,i-1,p2}); } REP(i,1,n){ rt[i]=rt[i-1]; const int sz=g[i].size(); for(int j=0;j<sz;++j)ins(rt[i],1,n,g[i][j].l,g[i][j].r,g[i][j].w); } while(m--){ int x=read(),y=read(); printf("%lld\n",ask(rt[x-1],rt[y],1,n,x,y)); } return 0; }