1. 程式人生 > 實用技巧 >6854. 【2020.11.04提高組模擬】古老的序列問題

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;
}