zoukankan      html  css  js  c++  java
  • [提高组集训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("");
    }
    
  • 相关阅读:
    序列JSON数据和四种AJAX操作方式
    jquery.validate和jquery.form.js实现表单提交
    JQuery Validate使用总结1:
    HOWTO: Include Base64 Encoded Binary Image Data (data URI scheme) in Inline Cascading Style Sheets (CSS)(转)
    SharePoint 2007 使用4.0 .Net
    动态IP解决方案
    取MS CRM表单的URL
    从Iframe或新开的窗口访问MS CRM 2011(转)
    Toggle or Hidden MS CRM Tab
    Windows 2008下修改域用户密码
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/15365030.html
Copyright © 2011-2022 走看看