• 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
    ",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
    ",
    //            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
    ",ans);
        return 0;
    }
  • 相关阅读:
    操作系统:进程同步
    操作系统:线程的概念
    操作系统:进程的概念与控制
    操作系统:操作系统概述
    CTF-WEB:攻防世界 ics-05(preg_replace() 函数 /e 漏洞)
    《剑指 Offer》学习记录:题 11:旋转数组的最小数字
    《剑指 Offer》学习记录:题 28:对称二叉树
    Linux为什么不是硬实时
    普通线程和内核线程
    linux内核栈和用户栈
  • 原文地址:https://www.cnblogs.com/Narh/p/9716621.html
Copyright © 2020-2023  润新知