[bzoj3745][分治]Norma

7 篇文章 0 订阅

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,r l , r ,找到他的中间点 mid m i d
枚举 l mid l   m i d ,我们的目标是对于所有 [l,mid] [ l , m i d ] 找出他在所有 [mid+1,r] [ m i d + 1 , r ] 区间内的答案
对于每个 i i ,维护两个指针u,v(u,v<=r)
满足 [mid+1,u] [ m i d + 1 , u ] 中的数全部大于 [i,mid] [ i , m i d ] 的最小值且u最大
满足 [mid+1,v] [ m i d + 1 , v ] 中的数全部小于 [i,mid] [ i , m i d ] 的最大值且v最大
于是我们有三种讨论,不妨假设u < v,设 mn m n 表示 [i,mid] [ i , m i d ] 中的最小值, mx m x 表示 [i,mid] [ i , m i d ] 中的最大值
对于 [mid+1,u] [ m i d + 1 , u ] ,他的贡献为

j=mid+1umxmn+(ji+1) ∑ j = m i d + 1 u m x ∗ m n + ( j − i + 1 )

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

拆一下就变成了这样
mxj=u+1vmin[mid+1,j]jmin(mid+1,j)(i1) m x ∗ ∑ j = u + 1 v m i n [ m i d + 1 , j ] ∗ j − m i n ( m i d + 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 + 1 r m a x [ m i d + 1 , j ] ∗ m i n ( m i d + 1 , j ) ∗ j − m a x ( m i d + 1 , j ) ∗ m i n ( m i d + 1 , j ) ∗ ( i − 1 )

预处理前缀和仍然可以 O(1) O ( 1 )
然后又是愉快的 nlogn n l o g n

#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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值