[ 分治 ] [ COCI2015 ] BZOJ3745

分治到一个区间时枚举左端点,然后分类讨论一下就好了。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
inline char nc(){
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline void Read(int& x){
    char c=nc();
    for(;c<'0'||c>'9';c=nc());
    for(x=0;c>='0'&&c<='9';x=(x<<3)+(x<<1)+c-48,c=nc());
}
const int N=500010;
const int M=1000000000;
int k,n,m,p;
int a[N];
int mn[N],mx[N],smn[N],smx[N],imn[N],imx[N],smnmx[N],imnmx[N];
int Ans;
inline void Add(int& x,int y){
    x=(x+y)%M;
}
inline int Sum(int l,int r){
    return 1ll*(r+l)*(r-l+1)/2%M;
}
void Solve(int l,int r){
    if(l==r){
        Add(Ans,1ll*a[l]*a[l]%M);
//      printf("%d %d %d\n",l,r,a[l]*a[l]);
        return;
    }
    int Mid=l+r>>1;
    Solve(l,Mid);Solve(Mid+1,r);
    mn[Mid+1]=mx[Mid+1]=smn[Mid+1]=smx[Mid+1]=a[Mid+1];
    smnmx[Mid+1]=1ll*mn[Mid+1]*mx[Mid+1]%M;
    imnmx[Mid+1]=1ll*smnmx[Mid+1]*(Mid+2)%M;
    imn[Mid+1]=imx[Mid+1]=1ll*(Mid+2)*a[Mid+1]%M;
    for(int i=Mid+2;i<=r;i++){
        mn[i]=min(mn[i-1],a[i]);mx[i]=max(mx[i-1],a[i]);
        smn[i]=(smn[i-1]+mn[i])%M;smx[i]=(smx[i-1]+mx[i])%M;
        imn[i]=(imn[i-1]+1ll*mn[i]*(i+1))%M;imx[i]=(imx[i-1]+1ll*mx[i]*(i+1))%M;
        smnmx[i]=(smnmx[i-1]+1ll*mn[i]*mx[i])%M;
        imnmx[i]=(imnmx[i-1]+1ll*mn[i]*mx[i]%M*(i+1))%M;
    }
    int j=Mid,k=Mid;
    int Mn=a[Mid],Mx=a[Mid];
    imn[Mid]=smn[Mid]=imx[Mid]=smx[Mid]=0;
    imnmx[Mid]=smnmx[Mid]=0;
    for(int i=Mid;i>=l;i--){
    int Res=Ans;
        Mn=min(Mn,a[i]);Mx=max(Mx,a[i]);
        while(j<r&&mn[j+1]>=Mn)j++;
        while(k<r&&mx[k+1]<=Mx)k++;
        Add(Ans,1ll*Mn*Mx%M*Sum(Mid+2-i,min(j,k)-i+1)%M);
        if(j<k){
            Add(Ans,1ll*Mx*(imn[k]-imn[j])%M);
            Add(Ans,-1ll*Mx*i%M*(smn[k]-smn[j])%M);
            Add(Ans,(imnmx[r]-imnmx[k])%M);
            Add(Ans,-1ll*i*(smnmx[r]-smnmx[k])%M);
        }else{
            Add(Ans,1ll*Mn*(imx[j]-imx[k])%M);
            Add(Ans,-1ll*Mn*i%M*(smx[j]-smx[k])%M);
            Add(Ans,(imnmx[r]-imnmx[j])%M);
            Add(Ans,-1ll*i*(smnmx[r]-smnmx[j])%M);
        }
    }
}
int main(){
    Read(n);
    for(int i=1;i<=n;i++)Read(a[i]);
    Solve(1,n);
    cout<<(Ans+M)%M<<endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值