序列问题

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

.
.
.
.
.
分析
这题竟然用很神奇的分治?!

对于区间[x…y],将它分成三部分:
m = (x +y)/2
1.左右端点都在[x…m]里的。
2.左右端点都在[m + 1…y]里的。
3.左右端点在m的两旁。

前两个递归处理,考虑第三个怎么求,这是分治的常规套路。

先考虑区间[m + 1…y](右区间),以m+1为左端点,从左往右枚举右端点,min值会不断变小,max值会不断变大,将变化的地方存下来,分别放进两个数组里,设为a,b。

现在还要考虑区间[x…m](左区间),从m出发,从右往左枚举左端点l,记录下min值和max值,设为min_l,max_l。

最后需要将两个区间合并。

在a数组里找到代表的值第一个小于min_l的位置u(从左往右看),
在b数组里找到代表的值第一个大于max_l的位置v(从左往右看)。
这个可以二分。
由于min_l不断缩小,max_r不断变大,也可以直接维护个指针。

右端点r的取法接下来有四种情况:

1.r < min(u, v),min_[l…r] = min_l, min_[l…r] = min_r。

2.r >= max(u, v), min_[l…r] = [l…r]里的点到m+1的最小值,max_[l…r] = [l…r]里的点到m+1的最大值。

3…u <= v, u<=r < v,min_[l…r] = [l…r]里的点到m+1的最小值,max_[l…r] = min_r。

4.u >v, v<=r < u,min_[l…r] = min_l,max_[l…r] = [l…r]里的点到m+1的最大值。

1可以直接算。2、3、4维护前缀和就行了。

.
.
.
.

程序:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;

long long mo=1000000007;
long long ans=0;
long long n,a[500005],u[500005],v[500005],s1[500005],s2[500005],s3[500005];

void fz(long long x,long long y) 
{
    if (x>y) return;
    if (x==y)
	{
        ans=(ans+a[x]*a[x]%mo)%mo; 
        return;
    }
    long long mid=(x+y)/2;
    fz(x,mid); 
	fz(mid+1,y);
    long long uu=1,vv=1;
    u[1]=v[1]=mid+1;
    s1[mid]=s2[mid]=s3[mid]=0;
    s1[mid+1]=s2[mid+1]=a[mid+1];
    s3[mid+1]=a[mid+1]*a[mid+1];
    for (int i=mid+2;i<=y;i++)
	{
        if (a[i]<a[u[uu]]) u[++uu]=i;
		if (a[i]>a[v[vv]]) v[++vv]=i;
        s1[i]=(s1[i-1]+a[u[uu]])%mo;
        s2[i]=(s2[i-1]+a[v[vv]])%mo;
        s3[i]=(s3[i-1]+a[u[uu]]*a[v[vv]])%mo;
    }
    long long minn=2147483647,maxx=-2147483647,l=1,r=1;
    long long sum=ans;
    for (int i=mid;i>=x;i--) 
	{
        minn=min(minn,a[i]);
		maxx=max(maxx,a[i]);
        while (l<=uu&&minn<=a[u[l]]) l++;
        while (r<=vv&&maxx>=a[v[r]]) r++;
        long long l1,r1;
        if (l>uu) l1=y; else l1=u[l]-1;
        if (r>vv) r1=y; else r1=v[r]-1;
        if (min(l1,r1)>mid) ans+=(long long)(min(l1, r1)-mid)*minn%mo*maxx%mo;
        if (max(l1,r1)<y) ans+=s3[y]-s3[max(l1,r1)];
        if (l1<=r1) ans+=(long long)(s1[r1]-s1[l1])*maxx%mo; else ans+=(long long)minn*(s2[l1]-s2[r1])%mo;
        ans=(ans%mo+mo)%mo;
    }
}
int main() {
    freopen("seq.in","r",stdin);
    freopen("seq.out","w",stdout);
    scanf("%lld",&n);
    for (int i=1;i<=n;i++) 
		scanf("%lld",&a[i]);
    fz(1,n);
    printf("%lld",ans);
    fclose(stdin);
    fclose(stdout);
    return 0;
}
posted @ 2019-01-27 21:00  银叶草  阅读(84)  评论(0编辑  收藏  举报
Live2D