[bzoj3745][分治]Norma

Description

这里写图片描述

Input

第1行,一个整数N; 第2~n+1行,每行一个整数表示序列a。

Output

输出答案对10^9取模后的结果。

Sample Input

4

2

4

1

4

Sample Output

109

HINT

【数据范围】

N <= 500000

1 <= a_i <= 10^8

题解

还是分治
自己yy的东西t的一逼啊233333
对于一段区间l,rl,r,找到他的中间点midmid
枚举l midl mid,我们的目标是对于所有[l,mid][l,mid]找出他在所有[mid+1,r][mid+1,r]区间内的答案
对于每个ii,维护两个指针u,v(u,v<=r)
满足[mid+1,u][mid+1,u]中的数全部大于[i,mid][i,mid]的最小值且u最大
满足[mid+1,v][mid+1,v]中的数全部小于[i,mid][i,mid]的最大值且v最大
于是我们有三种讨论,不妨假设u < v,设mnmn表示[i,mid][i,mid]中的最小值,mxmx表示[i,mid][i,mid]中的最大值
对于[mid+1,u][mid+1,u],他的贡献为

j=mid+1umxmn+(ji+1)∑j=mid+1umx∗mn+(j−i+1)

显然可以直接O(1)O(1)
对于[u+1,v][u+1,v],他的贡献为
j=u+1vmxmin[mid+1,j](ji+1)∑j=u+1vmx∗min[mid+1,j]∗(j−i+1)

拆一下就变成了这样
mxj=u+1vmin[mid+1,j]jmin(mid+1,j)(i1)mx∗∑j=u+1vmin[mid+1,j]∗j−min(mid+1,j)∗(i−1)

预处理前缀和即O(1)O(1)
对于[v+1,r][v+1,r],他的贡献为
j=v+1rmax[mid+1,j]min(mid+1,j)jmax(mid+1,j)min(mid+1,j)(i1)∑j=v+1rmax[mid+1,j]∗min(mid+1,j)∗j−max(mid+1,j)∗min(mid+1,j)∗(i−1)

预处理前缀和仍然可以O(1)O(1)
然后又是愉快的nlognnlogn
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#define LL long long
#define lc now<<1
#define rc now<<1|1
using namespace std;
const LL mod=1e9;
inline int read()
{
    int f=1,x=0;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int n;
LL a[510000];
LL s1[510000],s2[510000],s3[510000],s4[510000],s5[510000],s6[510000],ans;
/*
mn[mid+1,j]*j
mn[mid+1,j]
mx[mid+1,j]*j
mn[mid+1,j]
mx[mid+1,j]*mn[mid+1,j]*j
mx[mid+1,j]*mn[mid+1,j]
*/
LL C(int s,int t){return ((LL)s+t)*((LL)t-s+1)/2%mod;}
void sol(int l,int r)
{
    if(l==r){ans=(ans+a[l]*a[l])%mod;return ;}
    int mid=(l+r)>>1;
    s1[mid]=s2[mid]=s3[mid]=s4[mid]=s5[mid]=s6[mid]=0;
    LL u1=a[mid+1],u2=a[mid+1];
    sol(l,mid);
    sol(mid+1,r);
    for(LL j=mid+1;j<=r;j++)
    {
        u1=min(u1,a[j]);u2=max(u2,a[j]);
        //int u1=findhh(1,1,n,mid+1,j,0),u2=findhh(1,1,n,mid+1,j,1);
        s1[j]=(s1[j-1]+(u1*j%mod))%mod;
        s2[j]=(s2[j-1]+u1)%mod;
        s3[j]=(s3[j-1]+(u2*j%mod))%mod;
        s4[j]=(s4[j-1]+u2)%mod;
        s5[j]=(s5[j-1]+u1*u2%mod*j%mod)%mod;
        s6[j]=(s6[j-1]+u1*u2%mod)%mod;
    }
//  for(int j=mid+1;j<=r;j++)printf("%lld %lld\n",s1[j],s2[j]);
    int u=mid,v=mid;
    LL mn=a[mid],mx=a[mid];
    for(LL i=mid;i>=l;i--)
    {
        mn=min(mn,a[i]);mx=max(mx,a[i]);
        //int mn=findhh(1,1,n,i,mid,0),mx=findhh(1,1,n,i,mid,1);
        while(u<r&&mn<a[u+1])u++;
        while(v<r&&mx>a[v+1])v++;
        ans=(ans+mx*mn%mod*C(mid-i+2,min(u-i+1,v-i+1))%mod)%mod;
        if(u<=v)
        {
            ans=(ans+mx*((s1[v]-s1[u]+mod)%mod-((i-1)*(s2[v]-s2[u]+mod)%mod)%mod)%mod+mod)%mod;
            ans=(ans+((s5[r]-s5[v]+mod)%mod)-((i-1)*(s6[r]-s6[v]+mod)%mod)+mod)%mod;
        }
        else
        {
            ans=(ans+mn*((s3[u]-s3[v]+mod)%mod-((i-1)*(s4[u]-s4[v]+mod)%mod)%mod)%mod+mod)%mod;
            ans=(ans+((s5[r]-s5[u]+mod)%mod)-((i-1)*(s6[r]-s6[u]+mod)%mod)+mod)%mod;
        }
    }
}
int main()
{
//  freopen("a.in","r",stdin);
//  freopen("a.out","w",stdout);
    n=read();
    for(int i=1;i<=n;i++)a[i]=read();
    sol(1,n);
    printf("%lld\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值