6854. 【2020.11.04提高组模拟】古老的序列问题

一个数列,若干次询问,每次询问\([l,r]\)区间中所有子区间的\(max*min\)的和。

\(n,Q\le 10^5\)


老套路,维护个单调递增的栈和单调递减的栈,枚举右端点,维护每个左端点的贡献。搞个历史和,但是由于这里维护的是乘积,所以要维护各种各样的信息,比较复杂。反正我是没有写出来。

另一种做法:分治,将每个询问拆成\(O(\log n)\)个子问题,表示一个区间\([l,r]\)中,左端点在左半边,右端点在右半边的贡献。

对右区间搞个前缀\(max\)\(min\),枚举左端点,求出左边的最大值和最小值,计算最值在右边的分界点(左边用左半边的最值,右边用右半边的前缀最值),这样就可以分成三个区间(实际上是四种区间)。对这四种区间分别用数据结构维护,支持区间加系数和区间询问和。直接树状数组即可。

时间\(O(n\lg^2 n)\)


using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#define N 100005
#define ll long long
#define mo 1000000007
#define INF 1000000000
int n,m;
ll s[N];
struct Query{int l,r,t;};
vector<Query> q[N*4];
bool cmpp(Query x,Query y){return x.l>y.l;}
ll f[N*4],ans[N];
struct TA{
	int len;
	ll t[N];
	void init(int _n=n){
		len=_n;
		memset(t,0,sizeof(ll)*(len+1));
	}
	void add(int x,ll c){
		if (!c) return;
		for (;x<=len;x+=x&-x)
			(t[x]+=c)%=mo;
	}
	ll query(int x){
		ll res=0;
		for (;x;x-=x&-x)
			res+=t[x];
		return res%mo;
	}
};
struct TA2{
	int len,offset;
	TA t0,t1;
	ll p[N];
//	ll s[N];
	void init(int _offset,int _n=n,ll *_p=NULL){
		offset=_offset,len=_n;
		t0.init(len),t1.init(len);
//		if (_p==NULL) for (int i=1;i<=len;++i) p[i]=1;
//		else for (int i=1;i<=len;++i) p[i]=_p[i+offset];
//		memset(s,0,sizeof(ll)*(len+1));
		if (_p==NULL) for (int i=1;i<=len;++i) p[i]=i;
		else for (int i=1;i<=len;++i) p[i]=(p[i-1]+_p[i+offset])%mo;
	}
	void add(int l,int r,ll c){
		if (l>r) return;
		l-=offset,r-=offset;
//		for (int i=l;i<=r;++i)
//			(s[i]+=c)%=mo;
		t1.add(l,c),t1.add(r+1,mo-c);
		t0.add(l,mo-c*p[l-1]%mo);
		t0.add(r+1,c*p[r]%mo);
	}
	ll query(int r){
		r-=offset;
//		ll res=0;
//		for (int i=1;i<=r;++i)
//			(res+=p[i]*s[i])%=mo;
//		return res;
		return (t1.query(r)*p[r]+t0.query(r))%mo;
	}
} _,A,B,AB;
void divide(int k,int l,int r){
	if (l==r){
		f[k]=s[l]*s[l]%mo;
		for (int i=0;i<q[k].size();++i)
			(ans[q[k][i].t]+=f[k])%=mo;
		q[k].clear();
		return;	
	}
	static vector<Query> p;
	p.clear();
	int mid=l+r>>1;
	for (int i=0;i<q[k].size();++i)
		if (q[k][i].l<=mid && mid<q[k][i].r && !(q[k][i].l==l && q[k][i].r==r))
			p.push_back(q[k][i]);
	sort(p.begin(),p.end(),cmpp);
	static ll a[N],b[N],ab[N];
	a[mid]=INF,b[mid]=-INF;
	for (int i=mid+1;i<=r;++i){
		a[i]=min(a[i-1],s[i]);
		b[i]=max(b[i-1],s[i]);
		ab[i]=a[i]*b[i]%mo;
	}
	int len=r-mid;
	_.init(mid,len),A.init(mid,len,a),B.init(mid,len,b),AB.init(mid,len,ab);
	int al=mid,bl=mid;
	ll la=INF,lb=-INF;
	for (int i=mid,j=0;i>=l;--i){
		la=min(la,s[i]),lb=max(lb,s[i]);
		for (;al<r && a[al+1]>la;++al);
		for (;bl<r && b[bl+1]<lb;++bl);
		_.add(mid+1,min(al,bl),la*lb%mo);
		if (bl<al) B.add(min(al,bl)+1,al,la);
		if (al<bl) A.add(min(al,bl)+1,bl,lb);
		AB.add(max(al,bl)+1,r,1);
		for (;j<p.size() && p[j].l==i;++j)
			(ans[p[j].t]+=_.query(p[j].r)+A.query(p[j].r)+B.query(p[j].r)+AB.query(p[j].r))%=mo;
	}
	(f[k]+=_.query(r)+A.query(r)+B.query(r)+AB.query(r))%=mo;
	p.clear();
	for (int i=0;i<q[k].size();++i){
		int L=q[k][i].l,R=q[k][i].r;
		if (L==l && R==r) continue;
		if (R<=mid)
			q[k<<1].push_back(q[k][i]);
		else if (L>mid)
			q[k<<1|1].push_back(q[k][i]);
		else{
			q[k<<1].push_back((Query){L,mid,q[k][i].t});
			q[k<<1|1].push_back((Query){mid+1,R,q[k][i].t});
		}
	}
	divide(k<<1,l,mid);
	divide(k<<1|1,mid+1,r);
	f[k]+=f[k<<1]+f[k<<1|1];
	for (int i=0;i<q[k].size();++i)
		if (q[k][i].l==l && q[k][i].r==r)
			(ans[q[k][i].t]+=f[k])%=mo;
	q[k].clear();
//	printf("f[%d]=%lld\n",k,f[k]);
}
int main(){
//	freopen("in.txt","r",stdin);
//	freopen("out.txt","w",stdout);
	freopen("sequence.in","r",stdin);
	freopen("sequence.out","w",stdout);
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;++i)
		scanf("%lld",&s[i]);
	for (int i=1;i<=m;++i){
		int l,r;
		scanf("%d%d",&l,&r);
		q[1].push_back((Query){l,r,i});	
	}
	divide(1,1,n);
	for (int i=1;i<=m;++i)
		printf("%lld\n",ans[i]);
	return 0;
}
posted @ 2020-11-05 19:44  jz_597  阅读(441)  评论(0编辑  收藏  举报