2019牛客暑期多校训练营(第二场)J

题意

给一个长度为1e9的只包含1和-1的数列,1的个数不超过1e7,计算有多少对\((l,r)\)满足\(\sum_{i=l}^r a[i]>0\)

分析

dp求出每段连续的1最右端为右端点的最大子段和和最左端为左端点的最大子段和,可以得出这段1往左或右最远能扩到哪里,将相接的连续1段合并,合并后的每段区间和差值不会超过1e7,每段分别用桶来计数,细节很多要仔细想想..

Code

#include<bits/stdc++.h>
#define fi first
#define se second
#define pb push_back
#define ll long long
using namespace std;
const int inf=1e7+5;
const int mod=1e9+7;
const int maxn=1e7+10;
const int N=1e7+10;
int n,m;
int l[maxn],r[maxn];
int le[maxn],ri[maxn];
int tr[3*N];
int main(){
	//ios::sync_with_stdio(false);
	//freopen("in","r",stdin);
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d%d",&l[i],&r[i]);
	}
	int sum=0;
	l[0]=r[0]=-1;l[n+1]=r[n+1]=1e9;
	for(int i=1;i<=n;i++){
		sum+=r[i]-l[i]+1;
		if(sum>=l[i+1]-r[i]-1) sum-=l[i+1]-r[i]-1,ri[i]=l[i+1]-1;
		else ri[i]=r[i]+sum,sum=0;
	}sum=0;
	for(int i=n;i>=1;i--){
		sum+=r[i]-l[i]+1;
		if(sum>=l[i]-r[i-1]-1) sum-=l[i]-r[i-1]-1,le[i]=max(0,l[i]-sum);
		else le[i]=max(0,l[i]-sum),sum=0;
	}
	int mn=inf,mx=inf,pos=0,now=-1;
	ll ans=0,ret=0;
	for(int i=1;i<=n;i++){
		if(now<le[i]){
			for(int j=mn;j<=mx;j++) tr[j]=0;
			ret=0;
			mn=mx=pos=inf;
			now=le[i];
            tr[inf]=1;
		}
		while(now<l[i]){
			ret-=tr[--pos];
			tr[pos]++;
			now++;
			ans+=ret;
			mn=min(mn,pos);
		}
		while(now<=r[i]){
			ret+=tr[pos++];
			tr[pos]++;
			ans+=ret;
			now++;
			mx=max(mx,pos);
		}
		while(now<=ri[i]){
			ret-=tr[--pos];
			tr[pos]++;
			now++;
			ans+=ret;
			mn=min(mn,pos);
		}
	}
	printf("%lld\n",ans);
	return 0;
}
posted @ 2019-08-11 21:57  xyq0220  阅读(90)  评论(0编辑  收藏  举报