bzoj 3745: [Coci2015]Norma

Description

给定序列\(a_i\)

\[\sum_{i=1}^n \sum_{j=i}^n (j-i+1)\max\{a_i,a_{i+1}\cdots a_j\}\min\{a_i,a_{i+1}\cdots a_j\}\]

Input

第1行,一个整数N;

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

Output

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

Sample Input

4

2 4 1 4

Sample Output

109

【数据范围】

\(N \le 500000\)

\(1 \le a_i \le 10^8\)



我还有什么话可以说呢,都是细节问题。细节什么的最讨厌了

维护前缀\(sum1,sum2,sum3\)分别是\([0]\)里记录前缀中每一个数的\(a_i*i\)后的和,\([1]\)里记录每一个数在区间里的前缀和,这样求一个区间的贡献可以表示成\(sum[i][0]-sum[i][1]*j\)

\(j\)是当前点的编号,于是这样玄学的复杂度>_<

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;
#define ll long long
inline ll read(){
    ll an=0;
    char ch=getchar();
    while(ch<‘0‘||ch>‘9‘){ch=getchar();}
    while(‘0‘<=ch&&ch<=‘9‘){an=an*10+(ch^48);ch=getchar();}
    return an;
}
const ll mod=1e9;
const ll MAXn=500000+2333;
ll ans;
ll n;
ll sum1[MAXn][2],sum2[MAXn][2],sum3[MAXn][3],a[MAXn];
/*
\sum_{i=1}^n\sum_{j=i}^n(j-i+1)
\MAX\{a_i,a_{i+1}\cdots a_j\}
\MIN\{a_i,a_{i+1}\cdots a_j\}
*/
inline void prepare(){
    n=read();
    for(ll i=1;i<=n;i++)a[i]=read();
}
inline void solve(ll l,ll r){
    ll mid=l+r>>1;
    if(l==r){
        ans=(ans%mod+a[l]*a[l]%mod)%mod;
        return;
    }
    sum1[mid][0]=sum1[mid][1]=0;
    sum2[mid][0]=sum2[mid][1]=0;
    sum3[mid][0]=sum3[mid][1]=0;
    ll ma=-1,mi=1e9;
    for(ll i=mid+1;i<=r;i++){
        ma=max(ma,a[i]);
        mi=min(mi,a[i]);
        sum1[i][0]=ma%mod*mi%mod*i%mod;
        sum1[i][1]=ma*mi%mod;
        sum2[i][0]=mi*i%mod;sum2[i][1]=mi;
        sum3[i][0]=ma*i%mod;sum3[i][1]=ma;
        sum1[i][0]=(sum1[i][0]+sum1[i-1][0])%mod;
        sum1[i][1]=(sum1[i][1]+sum1[i-1][1])%mod;
        sum2[i][0]=(sum2[i][0]+sum2[i-1][0])%mod;
        sum2[i][1]=(sum2[i][1]+sum2[i-1][1])%mod;
        sum3[i][0]=(sum3[i][0]+sum3[i-1][0])%mod;
        sum3[i][1]=(sum3[i][1]+sum3[i-1][1])%mod;
    }
    ma=-1,mi=1e9;
    ll t1=a[mid+1],t2=a[mid+1];
    ll p1=mid,p2=mid;
    for(ll i=mid;i>=l;i--){
        ma=max(ma,a[i]);
        mi=min(a[i],mi);
        while(t1>=mi&&p1<r)p1++,t1=min(t1,a[p1+1]);
        while(t2<=ma&&p2<r)p2++,t2=max(t2,a[p2+1]);
        ll tmp;
        tmp=min(p1,p2);
        ll wi=(mid+2-i+tmp-i+1)*(tmp-mid)/2;
        ans=(ans+wi%mod*ma%mod*mi)%mod;
        tmp=max(p1,p2)+1;
        wi=sum1[r][0]-sum1[tmp-1][0];
        wi=(wi-(sum1[r][1]-sum1[tmp-1][1])%mod*(i-1))%mod;
        ans=(ans%mod+wi)%mod;
        if(p1<=p2){
            wi=(sum2[p2][0]-sum2[p1][0])%mod*ma%mod;
            wi=(wi-(sum2[p2][1]-sum2[p1][1])%mod*(i-1)%mod*ma+mod)%mod;
            ans=(ans+wi)%mod;
        }
        else{
            wi=(sum3[p1][0]-sum3[p2][0])%mod*mi;
            wi=(wi-(sum3[p1][1]-sum3[p2][1])%mod*(i-1)%mod*mi%mod+mod)%mod;
            ans=(ans+wi)%mod;
        }
    }
    solve(l,mid);
    solve(mid+1,r);
    ans=(ans%mod+mod)%mod;
}
int main(){
    prepare();
    solve(1,n);
    cout<<ans<<"\n";
    return 0;
}

时间: 2024-09-27 04:30:32

bzoj 3745: [Coci2015]Norma的相关文章

bzoj 3745 [Coci2015]Norma——序列分治

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3745 如果分治,就能在本层仅算过 mid 的区间了. 可以从中间到左边地遍历左边,给右边两个指针,表示第一个更新左边造成的最小值/最大值的位置. 两个位置共同的左边可以公式算长度,用左边的最值算:两个位置共同的右边可以预处理,处理出 算上长度(相对mid的)的最值乘积求和 与 不算长度的最值乘积求和(都是前缀),把前者加到答案里,后者乘上左边到mid的长度加到答案里即可:两个位置夹着的位置

bzoj 3745: [Coci2015]Norma【分治】

参考:https://blog.csdn.net/lych_cys/article/details/51203960 真的不擅长这种-- 分治,对于一个(l,r),先递归求出(l,mid),(mid+1,r),然后这个区间对答案贡献的就是经过mid的区间 我们先预处理出mid为l的右端点的mx*mn*len的前缀和与mx*mn的前缀和,然后枚举左端点,右端点维护两个下标j,k,分别表示mn和mx在左端点时的合法右端点 然后分三种情况处理,假设j<k 1.右端点在(mid+1,j)时,直接计算 2

[BZOJ3745][COCI2015]Norma(分治)

3745: [Coci2015]Norma Time Limit: 20 Sec  Memory Limit: 64 MBSubmit: 563  Solved: 249[Submit][Status][Discuss] Description Input 第1行,一个整数N: 第2~n+1行,每行一个整数表示序列a. Output 输出答案对10^9取模后的结果. Sample Input 4 2 4 1 4 Sample Output 109 [数据范围] N <= 500000 1 <=

【BZOJ3745】[Coci2015]Norma cdq分治

[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 题解:最近做这种题好像有点多啊~(虽然我基本上都没A). 比较直接的想法就是找出区间的最大值mid,然后分治处理[l,mid-1]和[mi

BZOJ 3881: [Coci2015]Divljak

Description 有两个集合\(ST\),\(S\)集合已知.有两个操作添加一个字符串到\(T\)询问T中有多少\(S_i\) \(n,q\leqslant 10^5,len(|S|),len(|T|)\leqslant 2\times 10^5\) Solution Trie树+DFS序. 添加一个字符串就要把Trie树上经过的节点及其fail树上的祖先+1将他差分一下,每次询问出现次数就变成了询问子树和.然后就是几条树梿的+1操作,因为差分过了,所以每个节点+1,相邻LCA-1 Cod

bzoj3745: [Coci2015]Norma

Description Input 第1行,一个整数N: 第2~n+1行,每行一个整数表示序列a. Output 输出答案对10^9取模后的结果. 预处理每个位置的数作为最小/大值向左延伸的最大距离,线段树维护序列的前缀的后缀min和后缀max以及这个前缀的后缀对答案的贡献,在前缀末尾加入一个数可以快速维护. #include<cstdio> typedef long long i64; const int N=500007,P=1000000000; char buf[N*20],*ptr=

BZOJ 3745

分治..... 之前就了解过这种分治统计答案的算法,对于当前的区间[l,r],我们考虑过中间的那条线的区间,这种题往往都存在单调性,我们发现min和max都是随位置单调的,我们枚举左端点x,然后维护两个指针p1,p2,表示[mid+1,p1/p2]这个区间的最值大于/小于[x,mid]的最值的最远的p1/p2,那么答案就可以分3段统计,一段是[mid+1,min(p1,p2)],右端点在这一部分的区间的min和max都一样,比较容易求出.另一段是[max(p1,p2)+1,r],在这一部分的区间

BZOJ 3881 Coci2015 Divljak fail树+树链的并

题目大意:给定两个字符串集合S和T,初始给定S集合中的所有字符串,不断向T集合中添加字符串,以及询问S集合中的某个字符串在T集合中的多少个字符串中出现过 神题- - 首先对S集合的所有字符串构建fail树 T集合中每加入一个字符串,我们就将这个字符串在AC自动机上跑一遍,并记录经过的所有节点 根据fail树的性质,这些节点到根路径上的所有节点的并集的出现次数都加了1 因此我们要求的就是树链的并 求法如下: 将所有节点按照DFS序排序 每个点到根的路径上的所有节点权值+1 相邻两个点的LCA到根的

bzoj 3881 [Coci2015]Divljak——LCT维护parent树链并

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3881 对 S 建 SAM ,每个 T 会让 S 的 parent 树的链并答案+1:在 T 走每一步的时候,走到的节点用 LCT access 一下,就能找到该点到 parent 根的链. 给链打标记.在 access 的过程中,如果遇到已经打过这个 T 标记的点,就停止 access . 注意实现的时候,在判断 fa[x] 有没有标记之前要先 splay(fa[x]) . #includ