P3722 [AHOI2017/HNOI2017]影魔

Captain_Paul

2018-10-10 15:48:15

Personal

题意:给出一个$1$到$n$的排列$a$,对于一对$i,j(1<=i<j<=n)$, 若对于任意$i<k<j$,都有$a[k]<min(a[i],a[j])$,那么价值为$p1$ 令$c=max\left\{a_k,k∈(i,j)\right\}$,若$min(a_i,a_j)<c<max(a_i,a_j)$,那么价值为$p2$ 多组询问所有满足$l<=i<j<=r$的$(i,j)$的价值之和 ------------ **树状数组(线段树)** **离线处理** 首先预处理出$i$左边右边**第一个大于$a_i$的位置** 分别记为$L_i,R_i$ 这个可以用**单调栈**实现 那么可以发现$i$的贡献: - 对于左端点在$L_i$,右端点在$R_i$的区间,有$p1$的贡献 - 对于左端点在$[L_i+1,i-1]$,右端点在$R_i$的区间,有$p2$的贡献 - 对于左端点在$L_i$,右端点在$[i+1,R_i-1]$的区间,有$p2$的贡献 把询问排好序,$i$从$1$到$n$扫 - 扫到$L_i$时,统计区间$[i+1,R_i-1]$的贡献 - 扫到$R_i$时,统计$L_i$和区间$[L_i+1,i-1]$的贡献 处理询问的时候用双指针,边修改边统计 对于一个询问的区间$[l,r]$,在扫到$l-1$时记录这个区间的贡献$sum1$,扫到$r$时记录这个区间的贡献$sum2$,那么答案就是$sum2-sum1$了 ``` #include<cstdio> #include<cstring> #include<cctype> #include<algorithm> #define reg register using namespace std; typedef long long ll; const int N=2e5+5; struct Q { int l,r,x,id,val; inline friend bool operator < (Q a,Q b) {return a.x<b.x;} }q[N<<1],p[N*3]; int n,m,p1,p2,cnt,a[N],stack[N],top,L[N],R[N]; ll c1[N],c2[N],ans[N]; inline int read() { int x=0,w=1; char c=getchar(); while (!isdigit(c)&&c!='-') c=getchar(); if (c=='-') c=getchar(),w=-1; while (isdigit(c)) { x=(x<<1)+(x<<3)+c-'0'; c=getchar(); } return x*w; } inline int lowbit(int x){return x&(-x);} inline void add(int x,ll k){for (reg int i=x;i<=n;i+=lowbit(i)) c1[i]+=k,c2[i]+=(ll)x*k;} inline ll query(int x,ll res=0){for (reg int i=x;i;i-=lowbit(i)) res+=(ll)(x+1)*c1[i]-c2[i]; return res;} int main() { n=read(),m=read(),p1=read(),p2=read(); for (reg int i=1;i<=n;a[i++]=read()); a[0]=a[n+1]=n+1; stack[++top]=0; for (reg int i=1;i<=n+1;i++) { while (top&&a[stack[top]]<a[i]) R[stack[top--]]=i; L[i]=stack[top]; stack[++top]=i; } for (reg int i=1;i<=m;i++) { int l=read(),r=read(); ans[i]=1ll*p1*(r-l); q[i]=(Q){l,r,l-1,i,-1}; q[i+m]=(Q){l,r,r,i,1}; } sort(q+1,q+2*m+1); for (reg int i=1;i<=n;i++) { if (L[i]&&R[i]<=n) p[++cnt]=(Q){L[i],L[i],R[i],cnt,p1}; if (L[i]<i-1&&R[i]<=n) p[++cnt]=(Q){L[i]+1,i-1,R[i],cnt,p2}; if (L[i]&&R[i]>i+1) p[++cnt]=(Q){i+1,R[i]-1,L[i],cnt,p2}; } sort(p+1,p+cnt+1); int now1=1,now2=1; while (!q[now1].x) ++now1; m<<=1; for (reg int i=1;i<=n,now1<=m;i++) { while (now2<=cnt&&p[now2].x==i) { add(p[now2].l,p[now2].val); add(p[now2].r+1,-p[now2].val); ++now2; } while (now1<=m&&q[now1].x==i) { ans[q[now1].id]+=q[now1].val*(query(q[now1].r)-query(q[now1].l-1)); ++now1; } } for (reg int i=1;i<=(m>>1);i++) printf("%lld\n",ans[i]); return 0; } ```