zoukankan      html  css  js  c++  java
  • 6854. 【2020.11.04提高组模拟】古老的序列问题

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

    (n,Qle 10^5)


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

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

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

    时间(O(nlg^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
    ",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
    ",ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    java中取两位小数 但不要四舍五入
    从字符串中提取数字 java正则表达式
    SQL实现 列转行(MySQL中)
    sql如何根据时间取出最新的数据记录
    动画 很精辟的
    week 与 strong区别 精辟的解释
    The executable was signed with invalid entitlements新设备run出现这个问题
    在iOS中创建静态库
    网址
    nginx单机1w并发设置
  • 原文地址:https://www.cnblogs.com/jz-597/p/13933566.html
Copyright © 2011-2022 走看看