BZOJ3745: [Coci2015]Norma

Description

 

Input

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

 

Output

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

 

Sample Input

4
2
4
1
4

Sample Output

109

【数据范围】
N <= 500000
1 <= a_i <= 10^8

HINT

 

Source

鸣谢 Dzy

 

真是********

考虑分治法,问题转化为如何解决跨过中点的答案。

对于一个序列[i,j],倒序枚举左端点i,则整个的maxv、minv的值有四种可能:

1.均在左一半取。

计算满足条件的右端最多到哪,记作cur,因为单调性,可以O(1)求。

ans+=maxvl*minvl*((mid+1-i+1)+(mid+2-i+1)+···+(cur-i+1))

变形,用等差数列求和就可以O(1)算了。

2.maxv在左取,minv在右取。

计算满足条件的右端最多到哪,记作curmn,因为单调性,可以O(1)求。

ans+=maxvl*[minvj*(j-i+1)+minvj+1*(j+1-i+1)+···+minvcurmn*(curmn-i+1)]

变形,维护一下minvj*j和minvj的前缀和就可以O(1)算了。

3.minv在右取,maxv在左取。

计算满足条件的右端最多到哪,记作curmx,因为单调性,可以O(1)求。

ans+=minvl*[maxvj*(j-i+1)+maxvj+1*(j+1-i+1)+···+maxvcurmx*(curmx-i+1)]

变形,维护一下maxvj*j和maxvj的前缀和就可以O(1)算了。

4.均在右一半取。

ans+=maxvj*minvj*(j-i+1)+···+maxvr*minvr*(r-i+1)。

变形,维护一下maxvj*minvj*j和maxvj*minvj的前缀和就可以O(1)算了。

说起来还真是简单呢!

#include<cstdio>
#include<cctype>
#include<queue>
#include<cmath>
#include<cstring>
#include<algorithm>
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define dwn(i,s,t) for(int i=s;i>=t;i--)
#define ren for(int i=first[x];i!=-1;i=next[i])
using namespace std;
const int BufferSize=1<<16;
char buffer[BufferSize],*head,*tail;
inline char Getchar() {
    if(head==tail) {
        int l=fread(buffer,1,BufferSize,stdin);
        tail=(head=buffer)+l;
    }
    return *head++;
}
inline int read() {
    int x=0,f=1;char c=Getchar();
    for(;!isdigit(c);c=Getchar()) if(c=='-') f=-1;
    for(;isdigit(c);c=Getchar()) x=x*10+c-'0';
    return x*f;
}
const int maxn=500010;
const int mod=1000000000;
typedef long long ll;
struct Pair {
    ll mx,mn;
}q1[maxn],q2[maxn];
Pair update(Pair a,ll v) {
    a.mx=max(a.mx,v);a.mn=min(a.mn,v);
    return a;
}
int n;
ll A[maxn],ans,S1[maxn],S2[maxn],S3[maxn],SS1[maxn],SS2[maxn],SS3[maxn];
ll sumlen(int b,int s,int t) {
    if(s>t) return 0;
    return (ll)(s-b+1+t-b+1)*(t-s+1)/2%mod;
}
void solve(int l,int r) {
    if(r-l+1<=10) {
        rep(i,l,r) {
            ll mx=A[i],mn=A[i];
            rep(j,i,r) {
                mx=max(mx,A[j]);mn=min(mn,A[j]);
                (ans+=(j-i+1)*mx*mn)%=mod;
            }
        }
        return;
    }
    int mid=l+r>>1;solve(l,mid);solve(mid+1,r);
    q1[mid]=(Pair){A[mid],A[mid]};dwn(i,mid-1,l) q1[i]=update(q1[i+1],A[i]);
    q2[mid+1]=(Pair){A[mid+1],A[mid+1]};rep(i,mid+2,r) q2[i]=update(q2[i-1],A[i]);
    q1[l-1]=q2[r+1]=(Pair){1e9,-1e9};
    S1[mid]=S2[mid]=S3[mid]=SS1[mid]=SS2[mid]=SS3[mid]=0;
    rep(i,mid+1,r) {
        S1[i]=(S1[i-1]+q2[i].mx*(i-mid))%mod;
        SS1[i]=(SS1[i-1]+q2[i].mx)%mod;
        S2[i]=(S2[i-1]+q2[i].mn*(i-mid))%mod;
        SS2[i]=(SS2[i-1]+q2[i].mn)%mod;
        S3[i]=(S3[i-1]+q2[i].mx*q2[i].mn%mod*(i-mid))%mod;
        SS3[i]=(SS3[i-1]+q2[i].mn*q2[i].mx)%mod;
    }
    int cur=mid+1,curmx=mid+1,curmn=mid+1;
    dwn(i,mid,l) {
        while(q2[cur].mx<=q1[i].mx&&q2[cur].mn>=q1[i].mn) cur++;
        while(q2[curmx].mx<=q1[i].mx) curmx++;
        while(q2[curmn].mn>=q1[i].mn) curmn++;
        ll t=ans;
        (ans+=sumlen(i,mid+1,cur-1)*q1[i].mx%mod*q1[i].mn%mod)%=mod;
        if(curmn-1>=curmx) (ans+=q1[i].mn*((S1[curmn-1]-S1[curmx-1]+mod+(SS1[curmn-1]-SS1[curmx-1]+mod)*(mid-i+1))%mod))%=mod;
        if(curmx-1>=curmn) (ans+=q1[i].mx*((S2[curmx-1]-S2[curmn-1]+mod+(SS2[curmx-1]-SS2[curmn-1]+mod)*(mid-i+1))%mod))%=mod;
        int p=max(curmx,curmn);
        if(p<=r) (ans+=S3[r]-S3[p-1]+mod+(SS3[r]-SS3[p-1]+mod)*(mid-i+1))%=mod;
    }
}
int main() {
    n=read();rep(i,1,n) A[i]=read();
    solve(1,n);printf("%lld\n",ans);
    return 0;
}
View Code

 

posted @ 2015-11-25 18:17  wzj_is_a_juruo  阅读(282)  评论(0编辑  收藏  举报