【分治计数】BZOJ3745 [Coci2015]Norma

题面在这里

分治计数的经典题目……

mid 右边的每个位置维护以下量:

imid 的前缀和

前缀 max min

前缀 max min 的前缀和 mx mn

mxmn 的前缀和

mxmn(imid) 的前缀和

mx(imid) mn(imid) 的前缀和

然后JXB讨论一下就好了

示例程序:

#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long LL;
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 int red(){
    int res=0,f=1;char ch=nc();
    while (ch<'0'||'9'<ch) {if (ch=='-') f=-f;ch=nc();}
    while ('0'<=ch&&ch<='9') res=res*10+ch-48,ch=nc();
    return res*f;
}

const int maxn=500005,INF=0x3f3f3f3f,tt=1000000000;
int n,a[maxn];
LL ans,s[maxn],smx[maxn],smn[maxn],ss[maxn],ssl[maxn],smxl[maxn],smnl[maxn],mx[maxn],mn[maxn];
void devide(int L,int R){
    if (L==R) {(ans+=(LL)a[L]*a[R]%tt)%=tt;return;}
    int mid=L+R>>1;
    devide(L,mid);devide(mid+1,R);
    s[mid]=smx[mid]=smn[mid]=mx[mid]=ss[mid]=ssl[mid]=smxl[mid]=smnl[mid]=0; mn[mid]=INF;
    for (int i=mid+1;i<=R;i++){
        mx[i]=max(mx[i-1],(LL)a[i]); mn[i]=min(mn[i-1],(LL)a[i]);
        smx[i]=(smx[i-1]+mx[i])%tt; smn[i]=(smn[i-1]+mn[i])%tt;
        s[i]=(s[i-1]+i-mid)%tt; ss[i]=(ss[i-1]+mx[i]*mn[i])%tt;
        ssl[i]=(ssl[i-1]+mx[i]*mn[i]%tt*(i-mid))%tt;
        smxl[i]=(smxl[i-1]+mx[i]*(i-mid))%tt; smnl[i]=(smnl[i-1]+mn[i]*(i-mid))%tt;
    }
    LL Max=0,Min=INF;
    for (int i=mid,jx=mid,jm=mid;i>=L;i--){
        Max=max(Max,(LL)a[i]);Min=min(Min,(LL)a[i]);
        while (jx<R&&mx[jx+1]<Max) jx++;
        while (jm<R&&mn[jm+1]>Min) jm++;
        int x=min(jx,jm),y=max(jx,jm);LL ii=mid-i+1;
        (ans+=(s[x]+ii*(x-mid))*Max%tt*Min)%=tt;
        if (jx<=jm)
         (ans+=(smxl[y]-smxl[x] + ii*(smx[y]-smx[x]))%tt*Min)%=tt;
        else
         (ans+=(smnl[y]-smnl[x] + ii*(smn[y]-smn[x]))%tt*Max)%=tt;
        (ans+=ssl[R]-ssl[y] + ii*(ss[R]-ss[y]))%=tt;
    }
}
int main(){
    n=red();
    for (int i=1;i<=n;i++) a[i]=red();
    devide(1,n);
    printf("%lld",(ans+tt)%tt);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值