Description
Input
输入文件名为seq.in。
首先输入n。
接下来输入n个数,描述序列 A。
Output
输出文件名为seq.out。
输出一行一个整数代表答案。
Sample Input
7 0 35 40 45 56 65 94
Sample Output
66636
Data Constraint
对于30%的数据,n<=5000
对于60%的数据,n<=50000
对于100%的数据,n<=500000,0<=A[i]<=10^9
ST加暴力枚举区间显然过不了,复杂度为O(n2)
考虑分治。对于区间[l,r],我们可以轻易求得[l,mid]和[mid+1,r]的值,关键在于求$\sum_{x\in[l,\,mid],\,y\in[mid+1,\,r]}f(x,\,y)*g(x,\,y)$
我们可以先枚举右端点r,求得每个点i到mid+1的最大值和最小值max[i]和min[i],然后从mid开始右往左枚举每一个左端点j,求得min_l,max_l,然后可以求得右端点中从左到右的第一个大于max_l,第一个小于min_l,的位置i,如图所示(我们假设min的位置在max位置的左边)
这样右边就被分成三段。
对于第一段(mid-min之间),由于最大值和最小值都是max_l,min_l,所以右端点这一段的值就是min_l*max_l*(min-mid)
对于第二段(min-max之间),由于最大值是max_l,最小值不定,不过我们可以把max_l提取出来,剩下的就是这段区间min_l的和了,所以我们可以用前缀和维护一下。smin[max-1]-smin[min-1]
对于第三段(max-r之间),由于最大值最小值都是不定,但是它们都在mid的右边,我们在分治的时候就已经算好了,直接加上去就好了 。 spul[r]-spul[max-1]
min在max右边同理。
1 #include<iostream> 2 #include<cstdio> 3 #define qaq 1000000007 4 using namespace std; 5 const int N=500005; 6 long long a[N],minn[N],maxx[N],sminn[N],smaxx[N],spul[N],ans; 7 int n; 8 void fz(long long l,long long r){ 9 if (r<l) return; 10 if (l==r){ 11 ans=(ans+a[l]*a[r])%qaq; 12 return; 13 } 14 long long mid=(l+r)>>1; 15 fz(l,mid); 16 fz(mid+1,r); 17 minn[mid]=1e12; 18 maxx[mid]=0; 19 sminn[mid]=0; 20 smaxx[mid]=0; 21 spul[mid]=0; 22 for (int i=mid+1;i<=r;i++){ 23 if (a[i]>maxx[i-1]) maxx[i]=a[i]; 24 else maxx[i]=maxx[i-1]; 25 if (a[i]<minn[i-1]) minn[i]=a[i]; 26 else minn[i]=minn[i-1]; 27 sminn[i]=sminn[i-1]+minn[i]; 28 smaxx[i]=smaxx[i-1]+maxx[i]; 29 spul[i]=(spul[i-1]+maxx[i]*minn[i])%qaq; 30 } 31 long long mi=mid; 32 long long ma=mid; 33 long long mis=1e12; 34 long long mas=0; 35 for (int i=mid;i>=l;i--){ 36 if (a[i]<mis) mis=a[i]; 37 if (a[i]>mas) mas=a[i]; 38 while ((mi<r)&&(minn[mi+1]>=mis)) mi++; 39 while ((ma<r)&&(maxx[ma+1]<=mas)) ma++; 40 if (mi<ma) 41 ans=(ans+mis%qaq*mas%qaq*(mi-mid)%qaq+(sminn[ma]-sminn[mi])%qaq*mas%qaq+(spul[r]-spul[ma])%qaq)%qaq; 42 else 43 ans=(ans+mis%qaq*mas%qaq*(ma-mid)%qaq+(smaxx[mi]-smaxx[ma])%qaq*mis%qaq+(spul[r]-spul[mi])%qaq)%qaq; 44 } 45 } 46 int main(){ 47 freopen("seq.in","r",stdin); 48 freopen("seq.out","w",stdout); 49 scanf("%d",&n); 50 for (int i=1;i<=n;i++) 51 scanf("%lld",&a[i]); 52 ans=0; 53 fz(1,n); 54 printf("%lld\n",ans); 55 return 0; 56 }
神奇的代码
乘的顺序很重要,乘的顺序很重要,乘的顺序很重要!!!(苦悲的ans那里乘的放到后面没有事先%%%就变负数waaaaaaaa了QAQ)