zoukankan      html  css  js  c++  java
  • [BZOJ4826] [HNOI2017] 影魔 单调栈 主席树

    题面

    因为是一个排列,所以不会有重复的。如果有重复就没法做了。一开始没有仔细看题目想了半天。

    发现,如果是第一种情况,那么边界(l)(r)就应该分别是整个区间的最大值和次大值。

    然后,对于那第二种情况, (l)(r)中,只有一个数是最大值,另一个数不可以是最大值和次大值。

    于是我们考虑从每一个合法区间内数里面选出一个代表来可以直接代表整个区间。

    用单调栈维护一下(lp[i])(rp[i])分别表示一个数左边和右边离(i)最近的大于之的数。

    然后对于第一种情况,发现相邻两个数一定是对的。然后发现,任何一个([lp[i],rp[i]])也是对的。因为可以保证边界上的数的最大与次大的性质。

    那么第二种呢?只要保证一个边界最大即可,因此就是([lp[i]+1..i-1,rp[i]])([lp[i],i+1..rp[i]-1])。可以保证不会重复,因为这里(i)实际上是每个区间的次大值,每个区间只会被其次大值统计一次。

    于是我们考虑把一个区间的边界位置分别作为二维的坐标,发现问题就变成了求以(l,l)(r,r)为顶点的正方形内的和。

    显然可以用扫描线。但是其实可以更方便。每一个位置只能是一个线段,所以我们直接用主席树维护一下即可。

    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<cstring>
    #include<vector>
    #define REP(i,a,n) for(register int i(a);i<=(n);++i)
    #define PER(i,a,n) for(register int i(a);i>=(n);--i)
    #define dbg(...) fprintf(stderr,__VA_ARGS__)
    const int SZ=(1<<21)+1;char ibuf[SZ],*iS,*iT,obuf[SZ+128],*oS=obuf,*oT=obuf+SZ-1;
    #ifndef ONLINE_JUDGE
    #define gc() getchar()
    #else
    #define gc() (iS==iT?(iT=(iS=ibuf)+fread(ibuf,1,SZ,stdin),iS==iT?EOF:*iS++):*iS++)
    #endif
    template<typename I>inline void read(I&x){char c=gc();int f=1;for(;c<'0'||c>'9';c=gc())c=='-'?f=-1:0;for(x=0;c>='0'&&c<='9';c=gc())x=(x<<1)+(x<<3)+(c&15);f==-1?x=-x:0;}
    inline void flush(){fwrite(obuf,1,oS-obuf,stdout);oS=obuf;}
    #define printf(...) oS>oT&&(flush(),1),oS+=sprintf(oS,__VA_ARGS__)
    typedef long long ll;typedef unsigned long long ull;
    template<typename A,typename B>inline char SMAX(A&a,const B&b){return a<b?a=b,1:0;}
    template<typename A,typename B>inline char SMIN(A&a,const B&b){return a>b?a=b,1:0;}
    
    const int N=200000+7;
    int n,m,T,p1,p2,a[N],x,y,q[N],tl,lp[N],rp[N];
    
    struct Node{int lc,rc,lps;ll val,add;}t[N*50];int RT[N],nod;//错误笔记:这里不是单调修改,所以仅仅开LOG被是不够的 
    inline void Insert(int &o,int L,int R,int l,int r,int k){
    	if(t[o].lps<T)t[++nod]=t[o],o=nod,t[o].lps=T;
    	if(l<=L&&R<=r)return (void)(t[o].add+=k,t[o].val+=(ll)k*(R-L+1));
    	int M=(L+R)>>1;if(l<=M)Insert(t[o].lc,L,M,l,r,k);if(r>M)Insert(t[o].rc,M+1,R,l,r,k);
    	t[o].val=t[t[o].lc].val+t[t[o].rc].val+t[o].add*(R-L+1);
    }
    inline ll Query(int o,int p,int L,int R,int l,int r,ll adv){
    	if(l<=L&&R<=r)return t[o].val-t[p].val+adv*(R-L+1);
    	int M=(L+R)>>1;if(r<=M)return Query(t[o].lc,t[p].lc,L,M,l,r,adv+t[o].add-t[p].add);else if(l>M)return Query(t[o].rc,t[p].rc,M+1,R,l,r,adv+t[o].add-t[p].add);
    	else return Query(t[o].lc,t[p].lc,L,M,l,r,adv+t[o].add-t[p].add)+Query(t[o].rc,t[p].rc,M+1,R,l,r,adv+t[o].add-t[p].add);
    }
    
    struct Pair{int x,y,w;};std::vector<Pair>g[N];
    inline void Preprocess(){
    	REP(i,1,n){
    		while(tl&&a[q[tl]]<a[i])--tl;
    		q[++tl]=i;lp[i]=q[tl-1];
    	}q[tl=0]=n+1;PER(i,n,1){
    		while(tl&&a[q[tl]]<a[i])--tl;
    		q[++tl]=i;rp[i]=q[tl-1];
    		if(i<n)g[i].push_back(Pair{i+1,i+1,p1});
    		if(lp[i]&&rp[i]<=n)g[rp[i]].push_back(Pair{lp[i],lp[i],p1});
    		if(rp[i]<=n&&i-1>=lp[i]+1)g[rp[i]].push_back(Pair{lp[i]+1,i-1,p2});
    		if(lp[i]&&i+1<=rp[i]-1)g[lp[i]].push_back(Pair{i+1,rp[i]-1,p2});
    	}
    	REP(i,1,n){
    		T=i;RT[i]=RT[i-1];int len=g[i].size();
    		REP(j,0,len-1)Insert(RT[i],1,n,g[i][j].x,g[i][j].y,g[i][j].w);
    	}
    }
    
    int main(){
    	read(n);read(m),read(p1),read(p2);
    	REP(i,1,n)read(a[i]);
    	Preprocess();
    	REP(i,1,m){
    		read(x),read(y);
    		printf("%lld
    ",Query(RT[y],RT[x-1],1,n,x,y,0));
    	}return flush(),0;
    }
    
  • 相关阅读:
    订单号生成规则
    mysql启动错误:mysql.sock丢失
    【转】Nginx服务并发过10万的Linux内核优化配置
    代理(正向代理)跟反向代理的区别
    php socket编程入门
    CentOS更改yum源与更新系统
    查看github.com上代码star排行
    html-3,table 表格标签 tr th td caption thead tbody tfoot 的简单使用
    html-2, a img ul li ol dl dt dd 标签与列表标签的简单使用
    html基本标签介绍及应用
  • 原文地址:https://www.cnblogs.com/hankeke/p/9971234.html
Copyright © 2011-2022 走看看