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

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3745

如果分治,就能在本层仅算过 mid 的区间了。

可以从中间到左边地遍历左边,给右边两个指针,表示第一个更新左边造成的最小值/最大值的位置。

两个位置共同的左边可以公式算长度,用左边的最值算;两个位置共同的右边可以预处理,处理出 算上长度(相对mid的)的最值乘积求和 与 不算长度的最值乘积求和(都是前缀),把前者加到答案里,后者乘上左边到mid的长度加到答案里即可;两个位置夹着的位置判断一下可以用左边的最大值还是最小值,所以要预处理右边最大值/最小值的算长度/不算长度前缀和,然后和公共右边一样的处理方法即可。

取模的地方要注意!可能加了一个东西,就不能再+mod、upd( ),而要直接upd( )。

注意把 l-1 位置的各种值赋成0。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int N=5e5+5,mod=1e9;
int n,a[N],ans,s0[N],s1[N],s2[N];//s:可加入ans,相对长度
int ml0[N],ml1[N],ml2[N];//ml:单纯乘积相加
int rdn()
{
    int ret=0;bool fx=1;char ch=getchar();
    while(ch>‘9‘||ch<‘0‘){if(ch==‘-‘)fx=0;ch=getchar();}
    while(ch>=‘0‘&&ch<=‘9‘) ret=(ret<<3)+(ret<<1)+ch-‘0‘,ch=getchar();
    return fx?ret:-ret;
}
void upd(int &x){x-=(x>=mod?mod:0);}
ll calc(int a){return (ll)a*(a+1)>>1ll;}
int dis(int x,int y){return y-x+1;}
void solve(int l,int r)
{
    if(l==r)
    {
        s2[l]=ml2[l]=(ll)a[l]*a[l]%mod;
        s1[l]=ml1[l]=s0[l]=ml0[l]=a[l];
        s2[l-1]=s1[l-1]=s0[l-1]=ml2[l-1]=ml1[l-1]=ml0[l-1]=0;//
        ans+=s2[l]; upd(ans);
        return;
    }
    int mid=l+r>>1;
    solve(l,mid); solve(mid+1,r);

    int lo=a[mid],hi=a[mid],p0=mid+1,p1=mid+1;
    for(int i=mid,cd=1;i>=l;i--,cd++)
    {
        hi=max(hi,a[i]); lo=min(lo,a[i]);
        while(a[p0]<=hi&&p0<=r) p0++;
        while(a[p1]>=lo&&p1<=r) p1++;
        int tl=min(p0,p1)-1,tr=max(p0,p1);
//        printf("l=%d r=%d hi=%d lo=%d p0=%d p1=%d\n",l,r,hi,lo,p0,p1);

        int d=dis(mid+1,tl);
        ans=(ans+( (ll)cd*d+calc(d) )%mod*lo%mod*hi)%mod;
//        printf("ans=%d ",ans);

        ans=(ans+s2[r]-s2[tr-1])%mod+mod; upd(ans);
        ans=ans+(ll)(ml2[r]-ml2[tr-1])*cd%mod;//cd not dis(i,tr-1)
        if(ans<0) ans+=mod; else upd(ans);//if
//        printf("ans=%d ",ans);

        if(p1<p0)//最小值已更新
        {
            ans=ans+(ll)hi*(s1[tr-1]-s1[tl])%mod;
            if(ans<0)ans+=mod; else upd(ans);
            ans=(ans+(ll)(ml1[tr-1]-ml1[tl])*cd%mod*hi%mod);//cd
            if(ans<0) ans+=mod; else upd(ans);//if
        }
        if(p0<p1)//最大值已更新
        {
//            printf("tl=%d tr=%d dis(i,tl)=%d s0[%d]-s0[%d]=%d lo=%d mml=%d\n",
//            tl,tr,dis(i,tl),tr-1,tl,s0[tr-1]-s0[tl],lo,ml0[tr-1]-ml0[tl]);
            ans=ans+(ll)lo*(s0[tr-1]-s0[tl])%mod;
            if(ans<0)ans+=mod; else upd(ans);
            ans=(ans+(ll)(ml0[tr-1]-ml0[tl])*cd%mod*lo%mod);
            if(ans<0) ans+=mod; else upd(ans);//if
        }
    }

    hi=lo=a[l];
    s2[l]=ml2[l]=(ll)hi*lo%mod;  s1[l]=ml1[l]=lo; s0[l]=ml0[l]=hi;
    for(int i=l+1,d=2;i<=r;i++,d++)
    {
        hi=max(hi,a[i]); lo=min(lo,a[i]);
        s2[i]=s2[i-1]+(ll)d*hi%mod*lo%mod; upd(s2[i]);
        s1[i]=s1[i-1]+(ll)d*lo%mod; upd(s1[i]);
        s0[i]=s0[i-1]+(ll)d*hi%mod; upd(s0[i]);

        ml2[i]=ml2[i-1]+(ll)hi*lo%mod; upd(ml2[i]);
        ml1[i]=ml1[i-1]+lo; upd(ml1[i]);
        ml0[i]=ml0[i-1]+hi; upd(ml0[i]);
    }
    s2[l-1]=s1[l-1]=s0[l-1]=ml2[l-1]=ml1[l-1]=ml0[l-1]=0;//
}
int main()
{
    n=rdn();
    for(int i=1;i<=n;i++)a[i]=rdn();
    solve(1,n);
    printf("%d\n",ans);
    return 0;
}

原文地址:https://www.cnblogs.com/Narh/p/9716621.html

时间: 2024-11-13 07:57:54

bzoj 3745 [Coci2015]Norma——序列分治的相关文章

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

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\) \

【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

[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 <=

BZOJ 4553 Tjoi2016&amp;Heoi2016 序列

Tjoi2016&Heoi2016序列 Description 佳媛姐姐过生日的时候,她的小伙伴从某宝上买了一个有趣的玩具送给他.玩具上有一个数列,数列中某些项的值 可能会变化,但同一个时刻最多只有一个值发生变化.现在佳媛姐姐已经研究出了所有变化的可能性,她想请教你 ,能否选出一个子序列,使得在任意一种变化中,这个子序列都是不降的?请你告诉她这个子序列的最长长度即可 .注意:每种变化最多只有一个值发生变化.在样例输入1中,所有的变化是: 1 2 3 2 2 3 1 3 3 1 1 31 2 4

bzoj 1176 [Balkan2007]Mokia - CDQ分治 - 树状数组

Description 维护一个W*W的矩阵,初始值均为S.每次操作可以增加某格子的权值,或询问某子矩阵的总权值.修改操作数M<=160000,询问数Q<=10000,W<=2000000. Input 第一行两个整数,S,W;其中S为矩阵初始值;W为矩阵大小 接下来每行为一下三种输入之一(不包含引号): "1 x y a" "2 x1 y1 x2 y2" "3" 输入1:你需要把(x,y)(第x行第y列)的格子权值增加a 输入

BZOJ 1046: [HAOI2007]上升序列 LIS -dp

1046: [HAOI2007]上升序列 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 3438  Solved: 1171[Submit][Status][Discuss] Description 对于一个给定的S={a1,a2,a3,…,an},若有P={ax1,ax2,ax3,…,axm},满足(x1 < x2 < … < xm)且( ax1 < ax2 < … < axm).那么就称P为S的一个上升序列.如果有多

BZOJ 2599: [IOI2011]Race( 点分治 )

数据范围是N:20w, K100w. 点分治, 我们只需考虑经过当前树根的方案. K最大只有100w, 直接开个数组CNT[x]表示与当前树根距离为x的最少边数, 然后就可以对根的子树依次dfs并更新CNT数组和答案. ------------------------------------------------------------------------------------------ #include<bits/stdc++.h> using namespace std; typ

【bzoj 3675】[Apio2014]序列分割

Description 小H最近迷上了一个分隔序列的游戏.在这个游戏里,小H需要将一个长度为n的非负整数序列分割成k+1个非空的子序列.为了得到k+1个子序列,小H需要重复k次以下的步骤: 1.小H首先选择一个长度超过1的序列(一开始小H只有一个长度为n的序列--也就是一开始得到的整个序列): 2.选择一个位置,并通过这个位置将这个序列分割成连续的两个非空的新序列. 每次进行上述步骤之后,小H将会得到一定的分数.这个分数为两个新序列中元素和的乘积.小H希望选择一种最佳的分割方式,使得k轮之后,小