zoukankan      html  css  js  c++  java
  • BZOJ.4826.[AHOI/HNOI2017]影魔(树状数组/莫队 单调栈)

    BZOJ
    LOJ
    洛谷

    之前看(mjt)用莫队写了,以为是一种正解,码了3h结果在LOJ T了没A= = 心态爆炸(upd:发现是用C++11(NOI)交的,用C++11交就快一倍了...)
    深刻的体会到什么叫写bug...比着一个数据调,调对了询问([1,5])又要调询问([2,7]),调过了([2,7])发现([1,5])又不对...(如此循环*n次)


    莫队 前缀和 单调栈:(非正解,不开O2 70分,开O2以及BZOJ算总时限可以A)
    可以先做一下HNOI2016 序列,感觉算是那道题的加强版,因为要多维护好多东西。。
    做完那题就容易发现我们需要维护什么了。假设我们要从([l,r-1])转移到([l,r]),首先找到区间最大值(p)的位置,然后([l,p])之间的贡献很好算,维护两个前缀和表示前面/后面的单调子序列中,比它大的有多少个。麻烦的是([p+1,r])里面的...
    刚开始想的是再维护两个前缀和,查询的时候求一下([L[r]+1,r-1])(L[x])(x)左边第一个比(A_x)大的数)中的最大值,再同样统计一下。
    然而细节贼特么多,写+调了3h,开O2过了,感动。
    对比(mjt)的代码发现orz,莫队转移的时候多了一次查询最小值对效率影响特别大。。
    又想了想发现一个前缀和就可以,然后又改+调+颓了2h。但是跑的比较快啦不开O2也有(70)分啦(mjt)(30)分2333)。
    不是很好解释,但很好理解,只是细节问题。。
    复杂度(O(nsqrt n))


    正解:

    (x)左边第一个比它大的点为(L),右边第一个比它大的点为(R)。考虑三种情况:

    1. 对于区间([L,R]),答案为(P1)
    2. 对于左端点在(L),右端点在(x+1sim R-1)的区间,答案各是(P2)
    3. 对于左端点在(L+1sim x-1),右端点在(R)的区间,答案也都是(P2)
      (这里不算区间([L,x])([x,R])的贡献,在子区间里算。。)

    然后考虑离线,把询问按右端点排序。如果只有(1,3)两种情况,在(R)处,给每个对应范围内的左端点加上贡献即可。现在实际是求的一个左端点在([q_l,q_r])内的和,所以现在答案是更新到(R)时,区间([q_l,q_r])的和。
    对于第(2)种情况比较麻烦,要拆成(O(n))个单点加?
    考虑把它变成区间加。注意到右端点在(p)处统计(L)的贡献(P2),和在(L)处给右端点在(p)时的贡献加(P2)是一样的,所以就可以在(L)处给区间(x+1sim R-1)(P2)
    这样的话左端点的贡献可能会多算,更新到(L-1)处时,给([q_l,q_r])的询问减去([q_l,q_r])的和即可。

    另一种理解方式:
    也可以考虑把([l,r])的贡献看做平面上的点((l,r))。同样离线,如果只有(1,3),在横坐标(R)处的对应区间加上对应的答案,询问([l,r])就是横纵坐标都在([l,r])的矩形的和。
    对于(2),同上面的分析,变成在(L)处给对应右端点加上贡献。虽然((x,y))((y,x))的贡献相同,但这相当于把原先横着的长条变成竖着的。注意到每个询问都是对角线在(y=x)上的正方形,所以这么做是对的。

    两种都是同样的区间加、区间求和,可以树状数组。
    复杂度(O(nlog n))

    另外对于([i,i+1])这种区间的答案没有统计到,加上即可。

    再简单记一下树状数组区间修改、区间求和
    类似区间修改单点查询,维护差分数组(d_i)的前缀和,那么$$egin{aligned}sum[1..p]&=sum_{i=1}^psum_{j=1}^id_j&=sum_{i=1}^p(p-i+1)d_i&=(p+1)sum_{i=1}^pd_i-sum_{i=1}^picdot d_iend{aligned}$$

    维护(d_i)(icdot d_i)的前缀和即可(区间修改就是两个最一般的单点。不要想错...)。


    莫队 前缀和 单调栈:

    //26132kb	15528ms
    #include <cmath>
    #include <cstdio>
    #include <cctype>
    #include <algorithm>
    //#define gc() getchar()
    #define MAXIN 300000
    #define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
    typedef long long LL;
    const int N=2e5+5,INF=1<<30;
    
    int P1,P2,bel[N],A[N],ref[N],st[18][N],Log[N],sk[N],L[N],R[N],sl[N],sr[N];
    char IN[MAXIN],*SS=IN,*TT=IN;
    struct Quries
    {
    	int l,r,id;
    	bool operator <(const Quries &x)const
    	{
    		return bel[l]==bel[x.l]?r<x.r:bel[l]<bel[x.l];
    	}
    }q[N];
    
    inline int read()
    {
    	int now=0;register char c=gc();
    	for(;!isdigit(c);c=gc());
    	for(;isdigit(c);now=now*10+c-48,c=gc());
    	return now;
    }
    inline int Query(int l,int r)//return pos
    {
    	int k=Log[r-l+1];
    	return ref[std::max(st[k][l],st[k][r-(1<<k)+1])];//比写A[x]>A[y]?x:y 快0.4s+...
    }
    void Init_ST(const int n)
    {
    	for(int i=2; i<=n; ++i) Log[i]=Log[i>>1]+1;
    	for(int j=1; j<=Log[n]; ++j)
    		for(int t=1<<j-1,i=n-t; i; --i)
    			st[j][i]=std::max(st[j-1][i],st[j-1][i+t]);
    }
    inline LL UpdL(int l,int r)
    {
    	if(l==r) return 0;
    	int p=Query(l+1,r),R=std::min(p,::R[l]),tot=R-l,num=sl[l+1]-sl[R]+1;
    	return (LL)num*P1+(tot-num)*P2+(R<p?(sl[R]-sl[p])*P2:(::R[l]>r)?(r-p)*P2:0);
    }
    inline LL UpdR(int l,int r)
    {
    	if(l==r) return 0;
    	int p=Query(l,r-1),L=std::max(p,::L[r]),tot=r-L,num=sr[r-1]-sr[L]+1;
    	return (LL)num*P1+(tot-num)*P2+(L>p?(sr[L]-sr[p])*P2:(::L[r]<l)?(p-l)*P2:0);
    }
    
    int main()
    {
    	static LL Ans[N];
    
    //	freopen("sf.in","r",stdin);
    //	freopen("sf.out","w",stdout);
    
    	const int n=read(),Q=read(),P1=read(),P2=read(),size=sqrt(n); ::P1=P1,::P2=P2;
    	for(int i=1; i<=n; ++i) st[0][i]=A[i]=read(), ref[A[i]]=i, bel[i]=i/size;
    	for(int i=1; i<=Q; ++i) q[i]=(Quries){read(),read(),i};
    	std::sort(q+1,q+1+Q);
    
    	Init_ST(n);
    	A[sk[0]=0]=INF;
    	for(int i=1,top=0; i<=n; ++i)//sr[i]:递减子序列中,i左边有多少个比它大的数 sr2[i]:贡献2 
    	{
    		while(A[sk[top]]<=A[i]) --top;//R[sk[top--]]=i;
    		sr[i]=sr[L[i]=sk[top]]+1, sk[++top]=i;//sr2[i]=sr2[sk[top]]+(i-sk[top]-1)*P2+P1,
    	}
    	A[sk[0]=n+1]=INF;
    	for(int i=n,top=0; i; --i)
    	{
    		while(A[sk[top]]<=A[i]) --top;//L[sk[top--]]=i;
    		sl[i]=sl[R[i]=sk[top]]+1, sk[++top]=i;//sl2[i]=sl2[sk[top]]+(sk[top]-i-1)*P2+P1,
    	}
    	LL Now=0;
    	for(int l=1,r=0,i=1; i<=Q; ++i)
    	{
    		int ln=q[i].l,rn=q[i].r;
    		while(l>ln) Now+=UpdL(--l,r);
    		while(r<rn) Now+=UpdR(l,++r);
    		while(l<ln) Now-=UpdL(l++,r);
    		while(r>rn) Now-=UpdR(l,r--);
    		Ans[q[i].id]=Now;
    	}
    	for(int i=1; i<=Q; printf("%lld
    ",Ans[i++]));
    
    	return 0;
    }
    

    树状数组:

    //25340kb	2440ms
    #include <cmath>
    #include <cstdio>
    #include <cctype>
    #include <algorithm>
    //#define gc() getchar()
    #define MAXIN 300000
    #define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
    typedef long long LL;
    const int N=2e5+5,INF=1<<30;
    
    int A[N],sk[N],L[N],R[N];
    char IN[MAXIN],*SS=IN,*TT=IN;
    struct Quries
    {
    	int l,r,p,id,val;
    	bool operator <(const Quries &x)const
    	{
    		return p<x.p;
    	}
    }q[N<<1];
    struct Opt
    {
    	int l,r,p,val;
    	bool operator <(const Opt &x)const
    	{
    		return p<x.p;
    	}
    }opt[N*3];
    struct BIT
    {
    	int n,t1[N]; LL t2[N];
    	#define lb(x) (x&-x)
    	inline void Add(int p,int v)
    	{
    		for(int v2=p*v; p<=n; p+=lb(p)) t1[p]+=v,t2[p]+=v2;
    	}
    	inline void Modify(int l,int r,int v)
    	{
    		Add(l,v), Add(r+1,-v);
    	}
    	inline LL Query2(int x)
    	{
    		LL res1=0,res2=0;
    		for(int p=x; p; p^=lb(p)) res1+=t1[p],res2+=t2[p];
    		return res1*(x+1)-res2;
    	}
    	inline LL Query(int l,int r)
    	{
    		return Query2(r)-Query2(l-1);
    	}
    }T;
    
    inline int read()
    {
    	int now=0;register char c=gc();
    	for(;!isdigit(c);c=gc());
    	for(;isdigit(c);now=now*10+c-48,c=gc());
    	return now;
    }
    
    int main()
    {
    	static LL Ans[N];
    
    //	freopen("sf.in","r",stdin);
    //	freopen("sf.out","w",stdout);
    
    	const int n=read(),Q=read(),P1=read(),P2=read(),Q2=Q<<1;
    	for(int i=1; i<=n; ++i) A[i]=read();
    	for(int i=1,l,r,t=0; i<=Q; ++i)
    		l=read(), r=read(), Ans[i]+=(r-l)*P1, q[++t]=(Quries){l,r,l-1,i,-1}, q[++t]=(Quries){l,r,r,i,1};
    	std::sort(q+1,q+1+Q2);
    
    	int top=0; A[sk[0]=0]=INF;
    	for(int i=1; i<=n; ++i)
    	{
    		while(A[sk[top]]<=A[i]) R[sk[top--]]=i;
    		L[i]=sk[top], sk[++top]=i;
    	}
    	while(top) R[sk[top--]]=n+1;
    
    	int tot=0;
    	for(int i=1; i<=n; ++i)
    	{
    		int l=L[i],r=R[i];
    		if(l&&r<=n) opt[++tot]=(Opt){l,l,r,P1};
    		if(l&&i+1<r) opt[++tot]=(Opt){i+1,r-1,l,P2};//有左端点才会有这个贡献 
    		if(l+1<i&&r<=n) opt[++tot]=(Opt){l+1,i-1,r,P2};
    	}
    	std::sort(opt+1,opt+1+tot);
    
    	T.n=n, opt[tot+1].p=N, q[Q2+1].p=N;
    	int nq=1,no=1;
    	while(!q[nq].p) ++nq;//!
    	for(int i=1; i<=n&&nq<=Q2; ++i)
    	{
    		while(opt[no].p==i) T.Modify(opt[no].l,opt[no].r,opt[no].val), ++no;
    		while(q[nq].p==i) Ans[q[nq].id]+=q[nq].val*T.Query(q[nq].l,q[nq].r), ++nq;
    	}
    	for(int i=1; i<=Q; printf("%lld
    ",Ans[i++]));
    
    	return 0;
    }
    

    放一份第一次写的莫队:

    #include <cmath>
    #include <cstdio>
    #include <cctype>
    #include <algorithm>
    #define MAXIN 300000
    #define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
    typedef long long LL;
    const int N=2e5+5,INF=1<<30;
    
    int P1,P2,bel[N],A[N],ref[N],st[18][N],Log[N],sk[N],L[N],R[N],sl[N],sr[N];
    LL sl2[N],sr2[N];
    char IN[MAXIN],*SS=IN,*TT=IN;
    struct Quries
    {
    	int l,r,id;
    	bool operator <(const Quries &x)const
    	{
    		return bel[l]==bel[x.l]?r<x.r:bel[l]<bel[x.l];
    	}
    }q[N];
    
    inline int read()
    {
    	int now=0;register char c=gc();
    	for(;!isdigit(c);c=gc());
    	for(;isdigit(c);now=now*10+c-48,c=gc());
    	return now;
    }
    inline int Query(int l,int r)//return pos
    {
    	int k=Log[r-l+1];
    	return ref[std::max(st[k][l],st[k][r-(1<<k)+1])];
    }
    void Init_ST(const int n)
    {
    	for(int i=2; i<=n; ++i) Log[i]=Log[i>>1]+1;
    	for(int j=1; j<=Log[n]; ++j)
    		for(int t=1<<j-1,i=n-t; i; --i)
    			st[j][i]=std::max(st[j-1][i],st[j-1][i+t]);
    }
    inline LL UpdL(int l,int r)
    {
    	if(l==r) return 0;
    	int p=Query(l,r),R=std::min(r+1,::R[l]);
    	LL delta=0;
    	if(l+1<R)
    	{
    		int p2=Query(l+1,R-1);
    		delta=sl2[l+1]-sl2[p2]+(R-p2-1)*P2+P1;
    	}
    	return delta+(R>r?0:R==r?P1:P1+(sl[l]-sl[p]-1)*P2);
    }
    inline LL UpdR(int l,int r)
    {
    	if(l==r) return 0;
    	int p=Query(l,r),L=std::max(l-1,::L[r]);
    	LL delta=0;
    	if(L+1<r)
    	{
    		int p2=Query(L+1,r-1);
    		delta=sr2[r-1]-sr2[p2]+(p2-L-1)*P2+P1;
    	}
    	return delta+(L<l?0:L==l?P1:P1+(sr[r]-sr[p]-1)*P2);
    }
    
    int main()
    {
    	static LL Ans[N];
    
    	// freopen("sf.in","r",stdin);
    	// freopen("sf.out","w",stdout);
    
    	const int n=read(),Q=read(),P1=read(),P2=read(),size=sqrt(n); ::P1=P1,::P2=P2;
    	for(int i=1; i<=n; ++i) st[0][i]=A[i]=read(), ref[A[i]]=i, bel[i]=i/size;
    	for(int i=1; i<=Q; ++i) q[i]=(Quries){read(),read(),i};
    	std::sort(q+1,q+1+Q);
    
    	Init_ST(n);
    	int top=0; A[sk[0]=0]=INF;
    	for(int i=1,v; i<=n; ++i)//sr[i]:递减子序列中,i左边有多少个比它大的数 
    	{
    		while(A[sk[top]]<=A[i]) R[sk[top--]]=i;
    		sr[i]=sr[v=sk[top]]+1, sr2[i]=sr2[v]+(i-v-1)*P2+P1, sk[++top]=i;
    	}
    	while(top) R[sk[top--]]=n+1;
    	top=0, A[sk[0]=n+1]=INF;
    	for(int i=n,v; i; --i)
    	{
    		while(A[sk[top]]<=A[i]) L[sk[top--]]=i;
    		sl[i]=sl[v=sk[top]]+1, sl2[i]=sl2[v]+(v-i-1)*P2+P1, sk[++top]=i;
    	}
    	while(top) L[sk[top--]]=0;
    	LL Now=0;
    	for(int l=1,r=0,i=1; i<=Q; ++i)
    	{
    		int ln=q[i].l,rn=q[i].r;
    		while(l>ln) Now+=UpdL(--l,r);
    		while(r<rn) Now+=UpdR(l,++r);
    		while(l<ln) Now-=UpdL(l++,r);
    		while(r>rn) Now-=UpdR(l,r--);
    		Ans[q[i].id]=Now;
    	}
    	for(int i=1; i<=Q; printf("%lld
    ",Ans[i++]));
    
    	return 0;
    }
    
  • 相关阅读:
    虎虎的小尾巴:期货长线换月损失太大怎么处理?
    虎虎的小尾巴:做期货怎么看基本面? 2020-02-27
    C++开发--在Visual Studio2013中使用boost::split()编译过程中出现error C4996
    C++开发--在Visual Studio2013中使用boost库
    Android开发--ZZ:Android APK反编译详解(附图)
    [Python]小百合十大爬虫
    Android开发-- findViewById()方法得到空指针
    Android开发-- The content of the adapter has changed but ListView did not receive a notification
    Android开发-- 简单对话框
    Python学习--判断变量的数据类型
  • 原文地址:https://www.cnblogs.com/SovietPower/p/10434381.html
Copyright © 2011-2022 走看看