• 题解-COCI2015Norma


    Problem

    SPOJ-NORMA2 & bzoj3745

    题意概要:给定一个正整数序列 ({a_i}),求

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

    (nleq 5 imes 10^5)

    Solution

    这题正解是一个完美的 (O(nlog n)) 分治,但比较麻烦,鉴于这个分治做法已经漫天飞了,所以这里不讲那个算法

    我在考场上在最后二十分钟想到了并打出了另一个分治做法,非常很好写跑得也很快,最终可以 AC

    可以考虑对于一个序列 ({a_i}),找到其最大值 (mx) 与最小值 (mi),有大量区间都是以这两点为最值点的,而同时这些区间的左右端点分别都是连续的,所以可以考虑将这些区间一起计算

    具体的,若找到的最大值与最小值分别在 (p_1,p_2) 取到(不妨设 (p_1leq p_2)),则以这两者为最值点的区间 ([l,r]) 满足 (1leq lleq p_1,p_2leq rleq n),这些区间的长度和可以 (O(1)) 算出,也即可以 (O(1)) 算出这些区间的贡献

    进一步的,需要加上其他不是 同时以这两者为最值点 的区间贡献。设统计左右端点都在 ([l,r]) 内的区间贡献也即刚刚这一步处理为函数 (f(l,r)),则其他区间的贡献即 (f(l,p_2-1)+f(p_1+1,r)-f(p_1+1,p_2-1))(由于前面两个式子中重复计算了左右端点都在 ([p_1+1,p_2-1]) 内的区间贡献,所以需要第三个函数去减去这部分多余的贡献)

    所以现在可以得到一个基本的做法(统计 ([l,r]) 区间):

    • (O(1)) 找到区间最大最小值所在位置 (p_1,p_2(p_1leq p_2))
    • (O(1)) 统计左端点在 ([l,p_1])、右端点在 ([p_2,r]) 的区间的贡献
    • 分治统计区间 ([l,p_2-1],[p_1+1,r]),并减去 ([p_1+1,p_2-1]) 的答案

    这个做法慢成龟龟,然后我灵机一动:分治下去的区间不是会继续使用当前最值点为最值点吗?(即 ([l,p_2-1]) 会使用 (p_1) 为最值点,进而可能再次调用区间 ([p_1+1,p_2-1]),这里的统计就冗余了,如果加个记忆化那么原来每次分出三个区间就可以均摊成两个了……)

    然后就加了一下 (map) 的记忆化,极限数据只需要 (0.4s)

    之前证了一波伪的复杂度 (O(nlog n)),后来被同校 dalao 精心卡掉了 虽然构造了一个多小时

    实际上复杂度是 (O(n^2log n)) 的,那个 (log) 还是 (map) 的复杂度 没错这是个暴力,但很难卡满,在考试中、spoj和bzoj上都没能卡掉我♪(*)

    实际运行效率很高,未经st表优化的代码在bzoj上跑到 (mathrm{rank6}),比我写的正解快一倍,同时代码也很短很好写 毕竟是在十分钟内写完调完的,只有 (mathrm{1.2k})

    Code

    由于想到这个解法时时间紧迫,没来得及写 (st) 表做 (mathrm{rmq}) 但还是过掉了

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    template <typename _tp> inline _tp read(_tp&x){
        char c11=getchar(),ob=0;x=0;
        while(c11!='-'&&!isdigit(c11))c11=getchar();if(c11=='-')c11=getchar(),ob=1;
        while(isdigit(c11))x=x*10+c11-'0',c11=getchar();if(ob)x=-x;return x;
    }
    
    const int N=501000,p=1e9,inf=0x3f3f3f3f;
    int a[N],n;
    
    map <int,int> mp[N];
    
    inline int getsum(int l,int r){return 1ll*(l+r)*(r-l+1)/2%p;}
    inline int qm(int x){while(x<0)x+=p;while(x>=p)x-=p;return x;}
    
    int force(int l,int r){
        int res(0);
        for(int i=l;i<=r;++i){
            int mx=-inf,mi=inf;
            for(int j=i;j<=r;++j){
                mx=max(mx,a[j]);
                mi=min(mi,a[j]);
                res=qm(res+1ll*(j-i+1)*mi%p*mx%p);
            }
        }return res;
    }
    
    int solve(int l,int r){
        if(l>r)return 0;
        if(mp[l].find(r)!=mp[l].end())
            return mp[l][r];
        if(r-l<=10)
            return mp[l][r]=force(l,r);
        int mx=-inf,mxd;
        int mi=inf,mid;
        for(int i=l;i<=r;++i){
            if(a[i]>mx)mx=a[i],mxd=i;
            if(a[i]<mi)mi=a[i],mid=i;
        }
        int L=min(mxd,mid),dl=L-l+1;
        int R=max(mxd,mid),dr=r-R+1;
        int dx=R-L-1,res(0);
        if(dl>dr)swap(dl,dr);
        for(int i=1;i<=dl;++i)
            res=qm(res+getsum(i+dx+1,i+dx+dr));
        res=1ll*res*mx%p*mi%p;
        return mp[l][r]=qm(res+qm(solve(l,R-1)+solve(L+1,r))-solve(L+1,R-1));
    }
    
    int main(){
        read(n);
        for(int i=1;i<=n;++i)read(a[i]);
        printf("%d
    ",solve(1,n));
        return 0;
    }
    
  • 相关阅读:
    SRS之SrsRtmpConn::service_cycle详解
    SRS之SrsRtmpServer::connect_app详解
    SRS之RTMP连接处理线程conn:接收客户端推流
    SRS之RTMP handshake
    SRS之RTMP的TCP线程(即监听线程)
    CSPS模拟 77
    CSPS模拟 76
    CSPS模拟 75
    CSPS模拟 74
    CSPS模拟 73
  • 原文地址:https://www.cnblogs.com/penth/p/10624801.html
Copyright © 2020-2023  润新知