【NOIP2017模拟8.5】序列问题

时间:2022-12-17 13:28:28

Description

【NOIP2017模拟8.5】序列问题

 

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,如图所示【NOIP2017模拟8.5】序列问题(我们假设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]

minmax右边同理。

【NOIP2017模拟8.5】序列问题【NOIP2017模拟8.5】序列问题
 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)