• [提高组集训2021] 古老的序列问题


    一、题目

    有一个长度为 (n) 的整数序列 (a),你需要回答 (m) 个询问,每次给出 (L,R),求下列式子的值:

    [sum_{l=L}^Rsum_{r=l}^R(max_{i=l}^r s_i)cdot (min_{i=l}^rs_i) ]

    (n,mleq 10^5)

    二、解法

    解法一

    猫树分治的进阶版应用,我们把猫叔看作线段树,每次把询问在猫树上面下放,下放的方式:如果正好覆盖当前区间那么留在这个节点上,回溯时算答案;如果不经过中点那么直接下放;如果经过中点那么统计左边对右边的贡献。

    主要问题是算左对右的贡献,我们拿一个指针在左半部分从后往前移动,记录到中点的最小值和最大值分别是 (la,lb),那么在右半部分可以分出四个段,含义分别是:最值都在左边取得、最大值在左边取得、最小值在左边取得、最值都在右边取得。

    不难发现可以用双指针来找到这些段,然后对于右边的每个点维护左边的一个后缀对它的贡献,在移动左边指针的时候更新贡献即可,四个段对应的系数不同(如果在右边去最值那么要用右边的来乘,这个叫做系数),那么我们用四个线段树来维护系数即可,询问的时候是一个区间查询的过程。

    那么时间复杂度 (O(nlog^2n))要注意卡常啊

    解法二

    如果只有一次询问怎么办?移动右端点维护每个左端点的答案,把右端点贡献到询问上去即可。

    如果有多个询问怎么办?移动右端点维护每个右端点对应每个左端点的答案,把历史和贡献到询问上去即可。

    那么写两个单调栈(+)一个带区间乘法支持区间历史和查询的线段树即可,用矩阵乘法维护历史和,时间复杂度 (O(nlog n))

    三、总结

    猫树进阶应用,一言以蔽之:线段树加 (cdq) 分治,注意利用过中点这个性质!

    遇事不决,矩阵乘法,矩阵什么都可以维护!

    //cat-tree divide
    #include <cstdio>
    #include <vector>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    const int M = 100005;
    const int N = 400005;
    const int MOD = 1e9+7;
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,m,s[M],z[M],a[M],b[M],ab[M],ans[M],f[N];
    struct node
    {
    	int l,r,id;
    	bool operator < (const node &b) const
    	{
    		return l>b.l;
    	}
    };vector<node> q[N];
    struct seg
    {
    	int L,R,sum[N],sx[N],tag[N];
    	int mul(int x,int y) {return 1ll*x*y%MOD;}
    	void add(int i,int c)
    	{
    		sum[i]=(sum[i]+mul(sx[i],c))%MOD;
    		tag[i]=(tag[i]+c)%MOD;
    	}
    	void build(int i,int l,int r,int *a)
    	{
    		if(i==1) L=l,R=r;
    		sum[i]=tag[i]=0;
    		if(l==r)
    		{
    			sx[i]=a[l];
    			return ;
    		}
    		int mid=(l+r)>>1;
    		build(i<<1,l,mid,a);
    		build(i<<1|1,mid+1,r,a);
    		sx[i]=(sx[i<<1]+sx[i<<1|1])%MOD;
    	}
    	void down(int i)
    	{
    		if(!tag[i]) return ;
    		add(i<<1,tag[i]);
    		add(i<<1|1,tag[i]);
    		tag[i]=0; 
    	}
    	void ins(int i,int l,int r,int L,int R,int c)
    	{
    		if(L>r || l>R) return ;
    		if(L<=l && r<=R)
    		{
    			add(i,c);
    			return ;
    		}
    		int mid=(l+r)>>1;down(i);
    		ins(i<<1,l,mid,L,R,c);
    		ins(i<<1|1,mid+1,r,L,R,c);
    		sum[i]=(sum[i<<1]+sum[i<<1|1])%MOD;
    	}
    	int ask(int i,int l,int r,int L,int R)
    	{
    		if(L>r || l>R) return 0;
    		if(L<=l && r<=R) return sum[i];
    		int mid=(l+r)>>1;down(i);
    		return (ask(i<<1,l,mid,L,R)+
    		ask(i<<1|1,mid+1,r,L,R))%MOD;
    	}
    	int Ask(int x) {return ask(1,L,R,L,x);}
    }I,A,B,AB;
    void add(int &x,int y) {x=(x+y)%MOD;}
    void div(int i,int l,int r)
    {
    	if(l==r)
    	{
    		f[i]=1ll*s[l]*s[l]%MOD;
    		for(auto x:q[i])
    			add(ans[x.id],f[i]);
    		return ;
    	}
    	int mid=(l+r)>>1;a[mid]=MOD;b[mid]=0;
    	vector<node> v;
    	for(auto x:q[i])
    		if(x.l<=mid && x.r>mid && !(x.l==l && x.r==r))
    			v.push_back(x);
    	sort(v.begin(),v.end());
    	for(int i=mid+1;i<=r;i++)
    	{
    		z[i]=1;
    		a[i]=min(a[i-1],s[i]);
    		b[i]=max(b[i-1],s[i]);
    		ab[i]=1ll*a[i]*b[i]%MOD;
    	}
    	I.build(1,mid+1,r,z);A.build(1,mid+1,r,a);
    	B.build(1,mid+1,r,b);AB.build(1,mid+1,r,ab);
    	int pa=mid,pb=mid,la=MOD,lb=0,t=mid+1;
    	for(int i=mid,j=0;i>=l;i--)
    	{
    		la=min(la,s[i]);lb=max(lb,s[i]);
    		for(;pa<r && a[pa+1]>la;pa++);
    		for(;pb<r && b[pb+1]<lb;pb++);
    		I.ins(1,t,r,t,min(pa,pb),1ll*la*lb%MOD);
    		if(pa<pb) A.ins(1,t,r,pa+1,pb,lb);
    		if(pb<pa) B.ins(1,t,r,pb+1,pa,la);
    		AB.ins(1,t,r,max(pa,pb)+1,r,1);
    		while(j<v.size() && v[j].l>=i)
    		{
    			node o=v[j];
    			add(ans[o.id],(1ll*I.Ask(o.r)+A.Ask(o.r)
    			+B.Ask(o.r)+AB.Ask(o.r))%MOD);
    			j++;
    		}
    	}
    	f[i]=(1ll*I.Ask(r)+A.Ask(r)+B.Ask(r)+AB.Ask(r))%MOD;
    	for(auto x:q[i])
    	{
    		if(l==x.l && x.r==r) continue;
    		if(x.r<=mid) q[i<<1].push_back(x);
    		else if(x.l>mid) q[i<<1|1].push_back(x);
    		else
    		{
    			node t1=x,t2=x;
    			t1.r=mid;q[i<<1].push_back(t1);
    			t2.l=mid+1;q[i<<1|1].push_back(t2);
    		}
    	}
    	div(i<<1,l,mid);div(i<<1|1,mid+1,r);
    	f[i]=(1ll*f[i]+f[i<<1]+f[i<<1|1])%MOD;
    	for(auto x:q[i]) if(l==x.l && x.r==r)
    		add(ans[x.id],f[i]);
    }
    void write(int x)
    {
    	if(x<=9)
    	{
    		putchar(x+'0');
    		return ;
    	}
    	write(x/10);
    	putchar(x%10+'0');
    }
    signed main()
    {
    	freopen("sequence.in","r",stdin);
    	freopen("sequence.out","w",stdout);
    	n=read();m=read();
    	for(int i=1;i<=n;i++)
    		s[i]=read();
    	for(int i=1;i<=m;i++)
    	{
    		int l=read(),r=read();
    		q[1].push_back(node{l,r,i});
    	}
    	div(1,1,n);
    	for(int i=1;i<=m;i++)
    		write(ans[i]),puts("");
    }
    
    //segment tree maintaining matrix
    #include <cstdio>
    #include <vector>
    #include <cstring>
    #include <cassert>
    using namespace std;
    const int M = 100005;
    const int MOD = 1e9+7;
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,m,ans[M],a[M],sa[M],sb[M];
    struct node{int x,y;};vector<node> g[M];
    struct mat
    {
    	int a[2][2];
    	mat() {memset(a,0,sizeof a);}
    	void reset() {a[0][1]=a[1][0]=0;a[0][0]=a[1][1]=1;}
    	mat operator * (const mat &b) const
    	{
    		mat r;
    		for(int i=0;i<2;i++)
    			for(int j=0;j<2;j++)
    				for(int k=0;k<2;k++)
    					r.a[i][k]=(r.a[i][k]+
    					1ll*a[i][j]*b.a[j][k])%MOD;
    		return r;
    	}
    }tr[4*M],tag[4*M],ZXY;
    int qkpow(int a,int b)
    {
    	int r=1;
    	while(b>0)
    	{
    		if(b&1) r=1ll*r*a%MOD;
    		a=1ll*a*a%MOD;
    		b>>=1;
    	}
    	return r;
    }
    mat con(int c)
    {
    	mat r;
    	r.a[0][0]=c;r.a[1][1]=1;
    	return r;
    }
    void build(int i,int l,int r)
    {
    	tag[i].reset();
    	tr[i].a[0][0]=r-l+1;
    	if(l==r) return ;
    	int mid=(l+r)>>1;
    	build(i<<1,l,mid);
    	build(i<<1|1,mid+1,r);
    }
    void down(int i)
    {
    	if(tag[i].a[0][0]==1 && tag[i].a[0][1]==0
    	&& tag[i].a[1][0]==0 && tag[i].a[1][1]==1) return ;
    	tr[i<<1]=tr[i<<1]*tag[i];
    	tag[i<<1]=tag[i<<1]*tag[i];
    	tr[i<<1|1]=tr[i<<1|1]*tag[i];
    	tag[i<<1|1]=tag[i<<1|1]*tag[i];
    	tag[i].reset();
    }
    void add(int i,int l,int r,int L,int R,mat c)
    {
    	if(L>r || l>R) return ;
    	if(L<=l && r<=R)
    	{
    		tr[i]=tr[i]*c;
    		tag[i]=tag[i]*c;
    		return ;
    	}
    	int mid=(l+r)>>1;down(i);
    	add(i<<1,l,mid,L,R,c);
    	add(i<<1|1,mid+1,r,L,R,c);
    	tr[i].a[0][0]=(tr[i<<1].a[0][0]
    	+tr[i<<1|1].a[0][0])%MOD;
    	tr[i].a[0][1]=(tr[i<<1].a[0][1]
    	+tr[i<<1|1].a[0][1])%MOD;
    }
    int ask(int i,int l,int r,int L,int R)
    {
    	if(l>R || L>r) return 0;
    	if(L<=l && r<=R) return tr[i].a[0][1];
    	int mid=(l+r)>>1;down(i);
    	return (ask(i<<1,l,mid,L,R)+
    	ask(i<<1|1,mid+1,r,L,R))%MOD;
    }
    void write(int x)
    {
    	if(x<=9)
    	{
    		putchar(x+'0');
    		return ;
    	}
    	write(x/10);
    	putchar(x%10+'0');
    }
    signed main()
    {
    	freopen("sequence.in","r",stdin);
    	freopen("sequence.out","w",stdout);
    	n=read();m=read();
    	for(int i=1;i<=n;i++)
    		a[i]=read();
    	build(1,1,n);
    	for(int i=1;i<=m;i++)
    	{
    		int l=read(),r=read();
    		g[r].push_back(node{l,i});
    	}
    	int la=0,lb=0;mat I;
    	I.reset();I.a[0][1]=1;
    	for(int i=1;i<=n;i++)
    	{
    		while(la && a[sa[la]]<a[i])
    		{
    			int c=qkpow(a[sa[la]],MOD-2);
    			add(1,1,n,sa[la-1]+1,sa[la],con(c));
    			la--;
    		}
    		while(lb && a[sb[lb]]>a[i])
    		{
    			int c=qkpow(a[sb[lb]],MOD-2);
    			add(1,1,n,sb[lb-1]+1,sb[lb],con(c));
    			lb--;
    		}
    		add(1,1,n,sa[la]+1,i,con(a[i]));
    		add(1,1,n,sb[lb]+1,i,con(a[i]));
    		sa[++la]=i;sb[++lb]=i;
    		add(1,1,n,1,i,I);
    		for(auto t:g[i])
    			ans[t.y]=(ans[t.y]+ask(1,1,n,t.x,i))%MOD;
    	}
    	for(int i=1;i<=m;i++)
    		write(ans[i]),puts("");
    }
    
  • 相关阅读:
    读取xml文件(可执行文件根目录debug)
    c# winform textbox与combox让用户不能输入
    枚举类型
    值类型与引用类型
    error: failed to push some refs to 'https://git.oschina.net/bluede/TuShuGuanLi.g it'
    left join on 和where中条件的放置位置
    left join、right join、inner join、full join
    Union、Union All、Intersect、Minus
    分层设计的好处
    Hibernate查询方式
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/15365030.html
Copyright © 2020-2023  润新知