JZOJ.5231【NOIP2017模拟8.5】序列问题

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]

minmax右边同理。

 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)

posted @ 2017-08-07 19:15  ~Lanly~  阅读(230)  评论(0编辑  收藏  举报