zoukankan      html  css  js  c++  java
  • BZOJ.4540.[HNOI2016]序列(莫队/前缀和/线段树 单调栈 RMQ)

    BZOJ
    洛谷


    ST表的一二维顺序一定要改过来。
    改了就rank1了哈哈哈哈。自带小常数没办法


    (Description)

    给定长为(n)的序列(A_i)(q)次询问,每次给定(l,r),求(sumlimits_{i=l}^rsumlimits_{j=i}^rmin{A_i,A_{i+1},...,A_j})
    (n,qleq10^5)


    (Solution)

    莫队:
    这种区间询问问题考虑一下莫队。
    考虑移动右端点(r o r+1)的时候怎么更新答案。
    (r+1)左边第一个比(A_{r+1})小的数是(A_p),那么(Ans)+=((r+1-p)*A_{r+1});同理再找到(p)右边第一个比(A_p)小的数(A_{p'})(Ans)+=((p-p')*A_p);再找(p')左边第一个比(A_{p'})小的数......
    这样怎么快速确定一个边界呢?
    ([l,r+1])的最小值是(A_x)(用(RMQ)得到),处理到(x)位置后,(Ans)+=((x-l+1)*A_x)就可以了。
    而左边第一个比(A_i)小的数的位置可以单调栈维护出来,记为(L_i)
    因为每个数作为最小值时的区间是不会相交的,大概就是会形成一个树形结构。反正是可以用一个类似前缀和的东西维护,记为(sum_i=sum_{L_i}+(i-L_i)*A_i)(右端点在(i),左端点在([1,i])时的答案)。
    这样移动右端点(r o r+1)的时候,(Ans)+=(sum_{r+1}-sum_x+(x-l+1)*A_x),就可以(O(1))更新了。
    对于左端点,还要再反着维护一个(sum)
    复杂度(O(nlog n+nsqrt{n}))


    优化:
    对于询问([l,r]),令([l,r])的区间最小值为(A_p)
    那么对于左端点在([l,p]),右端点在([p,r])的子区间,最小值都是(A_p),所以(p)的贡献就是(A_p imes(p-l+1) imes(r-p+1))
    当子区间左右端点都在((p,r])时,答案就是(sum_r+sum_{r-1}+...+sum_{p+1}-sum_p imes(r-p))
    (s_i=sum_{j=1}^isum_j),那么答案就是(s_r-s_p-sum_p imes(r-p))
    同理对于子区间左右端点在([l,p))时,反着维护一个(s)
    预处理出来就可以在线(O(1))回答询问了,复杂度(O(nlog n))


    线段树:
    这儿写的挺详细的,想再写一写。
    这种(sum_{i=l}^rsum_{j=i}^r)的询问,把([l,r])中的子区间写出来。比如拿样例来说,(l=1, r=5),每个子区间的最小值为:
    (1 1 1 1 3\1 1 1 1\2 2 4\2 2\5)
    当询问区间为([l,r])时,答案就是第(1)行到第(r)行中,前(lsim r)列的和。
    也就是每次右端点由(r-1)移动到(r),是给区间(isim r, 1leq ileq r)分别加了一个值(minlimits_{j=i}^r{a_j}),也就是上面的三角中多了第(r)行。
    如果此时询问右端点就是(r),那答案就是([l,r])此时的区间和。

    观察每一行的区间加,每次(A_r)会覆盖(r)前面一段区间,然后就不变和上一次一样了。
    (A_r)会覆盖的区间就是([p+1,r])(p)(r)前面第一个比(A_r)小的位置,可以单调栈预处理。
    也就是我们把([p+1,r])(val)改成(A_r),然后所有位置的(sum)+=(val)

    怎么维护呢?
    记此时的区间(val)的和为(sum),区间长度为(len),要修改的值为(v)。不妨把([l,r])的答案记作(his),也就是([l,r])中所有点各历史版本的(val)的和。
    那么修改就是:(sum'=v*len),更新(his)就是:(his'=his+sum')(表示不理解为什么都写的(+sum)啊QAQ)。

    要用线段树高效维护区间修改,就得能维护标记合并。
    把转移用矩阵写出来,就是这样:$$egin{bmatrix}len & sum & his \end{bmatrix}
    egin{bmatrix}
    1 & a & c
    0 & b & d
    0 & 0 & 1
    end{bmatrix}=egin{bmatrix}len' & sum' & his' \end{bmatrix}$$

    维护(a,b,c,d)四个值。修改的参数就是(a=v,b=c=d=0),更新(his)的参数是(d=1,a=b=c=0),单位矩阵(初始化)是(b=1,a=c=d=0)
    虽然参数用不到(c),但是矩阵结合的时候会改变(c),但不会影响其他不维护的位置,所以要维护(c)
    这样利用矩阵的结合律,就可以维护标记合并了。

    复杂度(O(nlog n))

    这个实在懒得写了...没心情。(虽然我知道我几天就会给忘了)


    莫队:

    //13240kb	3876ms->2980ms
    #include <cmath>
    #include <cstdio>
    #include <cctype>
    #include <assert.h>
    #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=1e5+5,INF=0x7fffffff;
    
    int bel[N],A[N],sk[N],Log[N],pos[18][N];
    LL suml[N],sumr[N],Ans[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,f=1;register char c=gc();
    	for(;!isdigit(c);c=='-'&&(f=-1),c=gc());
    	for(;isdigit(c);now=now*10+c-48,c=gc());
    	return now*f;
    }
    inline int Min(int x,int y)
    {
    	return A[x]<=A[y]?x:y;
    }
    inline int Query(int l,int r)
    {
    	int k=Log[r-l+1];
    	return Min(pos[k][l],pos[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)
    			pos[j][i]=Min(pos[j-1][i],pos[j-1][i+t]);
    }
    inline LL UpdL(int l,int r)
    {
    	int p=Query(l,r);
    	return suml[l]-suml[p]+1ll*(r-p+1)*A[p];
    }
    inline LL UpdR(int l,int r)
    {
    	int p=Query(l,r);
    	return sumr[r]-sumr[p]+1ll*(p-l+1)*A[p];
    }
    
    int main()
    {
    	const int n=read(),Q=read(),size=sqrt(n);
    	for(int i=1; i<=n; ++i) bel[i]=i/size, A[i]=read(), pos[0][i]=i;
    	for(int i=1; i<=Q; ++i) q[i]=(Quries){read(),read(),i};
    
    	Init_ST(n), A[0]=-INF;
    	for(int i=1,top=0; i<=n; ++i)
    	{
    		while(A[sk[top]]>A[i]) --top;
    		sumr[i]=sumr[sk[top]]+1ll*(i-sk[top])*A[i], sk[++top]=i;
    	}
    	A[sk[0]=n+1]=-INF;
    	for(int i=n,top=0; i; --i)
    	{
    		while(A[sk[top]]>A[i]) --top;
    		suml[i]=suml[sk[top]]+1ll*(sk[top]-i)*A[i], sk[++top]=i;
    	}
    	std::sort(q+1,q+1+Q);
    	LL Now=0;
    	for(int i=1,l=1,r=0; i<=Q; ++i)
    	{
    		int ln=q[i].l,rn=q[i].r;
    		while(l>ln) Now+=UpdL(--l,r);//l的减 或 r的加 要先进行,否则就会出现l>r的情况 然后Query的时候就gg了 
    		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;
    }
    

    优化:

    //12444kb	700ms
    #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=1e5+5,INF=0x7fffffff;
    
    int A[N],sk[N],Log[N],pos[18][N];
    LL suml[N],sumr[N],sl[N],sr[N];
    char IN[MAXIN],*SS=IN,*TT=IN;
    
    inline int read()
    {
    	int now=0,f=1;register char c=gc();
    	for(;!isdigit(c);c=='-'&&(f=-1),c=gc());
    	for(;isdigit(c);now=now*10+c-48,c=gc());
    	return now*f;
    }
    inline int Min(int x,int y)
    {
    	return A[x]<=A[y]?x:y;
    }
    inline int Query(int l,int r)
    {
    	int k=Log[r-l+1];
    	return Min(pos[k][l],pos[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)
    			pos[j][i]=Min(pos[j-1][i],pos[j-1][i+t]);
    }
    
    int main()
    {
    	const int n=read(),Q=read();
    	for(int i=1; i<=n; ++i) A[i]=read(), pos[0][i]=i;
    
    	Init_ST(n), A[0]=-INF;
    	for(int i=1,top=0; i<=n; ++i)
    	{
    		while(A[sk[top]]>A[i]) --top;
    		sumr[i]=sumr[sk[top]]+1ll*(i-sk[top])*A[i], sr[i]=sr[i-1]+sumr[i];
    		sk[++top]=i;
    	}
    	A[sk[0]=n+1]=-INF;
    	for(int i=n,top=0; i; --i)
    	{
    		while(A[sk[top]]>A[i]) --top;
    		suml[i]=suml[sk[top]]+1ll*(sk[top]-i)*A[i], sl[i]=sl[i+1]+suml[i];
    		sk[++top]=i;
    	}
    
    	for(int i=1; i<=Q; ++i)
    	{
    		int l=read(),r=read(),p=Query(l,r);
    		printf("%lld
    ",1ll*A[p]*(p-l+1)*(r-p+1)+sr[r]-sr[p]-sumr[p]*(r-p)+sl[l]-sl[p]-suml[p]*(p-l));
    	}
    
    	return 0;
    }
    
  • 相关阅读:
    洛谷 P1260 工程规划(差分约束)
    洛谷 P3660 [USACO17FEB]Why Did the Cow Cross the Road III G(树状数组)
    [模板]单调队列
    [模板]LIS(最长上升子序列)
    洛谷 P2899 [USACO08JAN]手机网络Cell Phone Network(树形动规)
    如何求数字n的因数个数及因数和
    [模板]tarjan缩点+拓扑排序
    itext生成pdf(附带页眉,页脚,页码)
    工作总结03
    工作总结02(海报上传模块)
  • 原文地址:https://www.cnblogs.com/SovietPower/p/10286483.html
Copyright © 2011-2022 走看看