• 一类分治问题


    有一类关于区间最大值和最小值之类的问题,利用单调性,可以采用分治算法解决。

    SPOJ22343 Norma

    题意,给定一个数列,定义区间的代价为区间最大值、区间最小值、区间长度的乘积,求所有区间的代价和。

    既然是分治,我们肯定要处理一个数列跨过中点的答案。

    假设当前数列的中点为mid,我们从mid往前扫,扫到了i。

    然后根据单调性,我们越往左扫,最大值单调不降,最小值单调不增。

    那么我们可以在右边维护一个指针,表示满足最大值的区间的最靠右的端点。

    假设有这么一种情况,那么我们可以把区间拆成mid~p1,p1~p2,p2~r,三段。

    1、mid~p1,max和min都是已知的,max*min*(r-l+1)。

    2、p1~p2,min是已知的,我们需要算一个sigma(max)再乘上区间长度搞个前缀和算一下。

    3、maxmin都未知,我们还需要维护max*min*区间长度搞成前缀和。

    Code

    #include<iostream>
    #include<cstdio>
    #define N 500010
    #define inf 1e18
    using namespace std;
    typedef long long ll;
    const int mod=1e9;
    ll ans,a[N],mn[N][2],mi[N][2],ji[N][2];
    int n;
    inline ll calc(ll l,ll r){
        return ((l+r)*(r-l+1)/2)%mod;
    }
    void solve(int l,int r){
        if(l==r){(ans+=a[l]*a[l]%mod)%=mod;return;}
        ll mid=(l+r)>>1;
        solve(l,mid);solve(mid+1,r); 
        ll maxn=-inf,minn=inf;
        mn[mid][0]=mn[mid][1]=mi[mid][0]=mi[mid][1]=ji[mid][0]=ji[mid][1]=0;
        for(ll i=mid+1;i<=r;++i){
            maxn=max(maxn,a[i]);minn=min(minn,a[i]);
            mn[i][0]=(mn[i-1][0]+maxn)%mod;mn[i][1]=(mn[i-1][1]+maxn*(i-mid)%mod)%mod;
            mi[i][0]=(mi[i-1][0]+minn)%mod;mi[i][1]=(mi[i-1][1]+minn*(i-mid)%mod)%mod;
            ji[i][0]=(ji[i-1][0]+minn*maxn%mod)%mod;ji[i][1]=(ji[i-1][1]+minn*maxn%mod*(i-mid)%mod)%mod;
        }
        ll p=mid+1,q=mid+1;maxn=-inf;minn=inf;
        for(ll i=mid;i>=l;--i){
            minn=min(minn,a[i]);maxn=max(maxn,a[i]);
            while(p<=r&&a[p]>=minn)p++;
            while(q<=r&&a[q]<=maxn)q++;
            int ls=min(p,q),rs=max(p,q);
            (ans+=maxn*minn%mod*calc(mid+1-i+1,ls-i)%mod)%=mod;
            if(p<q)ans=(ans+((mi[rs-1][0]-mi[ls-1][0])*(mid-i+1)%mod+mi[rs-1][1]-mi[ls-1][1])%mod*maxn%mod+mod)%mod;
            if(p>q)ans=(ans+((mn[rs-1][0]-mn[ls-1][0])*(mid-i+1)%mod+mn[rs-1][1]-mn[ls-1][1])%mod*minn%mod+mod)%mod; 
            ans=((ans+(ji[r][0]-ji[rs-1][0])*(mid-i+1)%mod+(ji[r][1]-ji[rs-1][1])%mod)%mod+mod)%mod;
        }
    }
    int main(){
        scanf("%d",&n);
        for(int i=1;i<=n;++i)scanf("%lld",&a[i]);
        solve(1,n); 
        cout<<ans;
        return 0;
    } 

    CF526F

    化简题意,给定一个排列,求有多少区间满足max-min=r-l;

    和上题一样,考虑跨过中点的答案,因为我们要计算合法区间,所以这和max和min的分布情况有关。

    最大值最小值都在一侧的情况,max,min,l都已知,r是可以O(1)求出的,直接判断就好了。

    如果最大值最小值分布在两侧,我们可以在一边枚举最端点,在右边用双指针维护一个区间满足最大值在左边,最小值在右边。

    然后max-min=r-l  ->  max+l=r+min用桶维护这个式子。

    最大值最小值在右边,最小值在左边最大值在右边的情况同理。

    Code

    #include<iostream>
    #include<cstdio>
    #define N 300002
    #define inf 0x3f3f3f3f
    using namespace std;
    int ma[N],mi[N],a[N],l,r,tong[N<<1],n;
    long long ans;
    void solve(int l,int r){
        if(l==r){ans++;return;}
        int mid=(l+r)>>1;
        solve(l,mid);solve(mid+1,r);
        ma[mid]=0;mi[mid]=inf;
        for(int i=mid+1;i<=r;++i){
            ma[i]=max(ma[i-1],a[i]);
            mi[i]=min(mi[i-1],a[i]);
        }
        ma[mid]=mi[mid]=a[mid];
        for(int i=mid-1;i>=l;--i){
            ma[i]=max(ma[i+1],a[i]);
            mi[i]=min(mi[i+1],a[i]);
        }
        int p=mid+1,q;
        for(int i=mid;i>=l;--i){
            p=ma[i]-mi[i]+i;
            if(p<=r&&p>=mid+1&&ma[p]<ma[i]&&mi[p]>mi[i])ans++;
        }
        p=mid;
        for(int i=mid+1;i<=r;++i){
            p=i-ma[i]+mi[i];
            if(p>=l&&p<=mid&&ma[p]<ma[i]&&mi[p]>mi[i])ans++;
        } 
        p=q=mid;
        for(int i=mid+1;i<=r;++i){
            while(ma[p]<ma[i]&&p>=l)tong[mi[p]-p+n]++,p--;
            while(mi[q]>mi[i]&&q>p)tong[mi[q]-q+n]--,q--;
            ans+=tong[ma[i]-i+n];
        }
        while(q>p)tong[mi[q]-q+n]--,q--;
        p=q=mid+1;
        for(int i=mid;i>=l;--i){
            while(ma[p]<ma[i]&&p<=r)tong[mi[p]+p]++,p++;
            while(mi[q]>mi[i]&&q<p)tong[mi[q]+q]--,q++;
            ans+=tong[ma[i]+i];
        }
        while(q<p)tong[mi[q]+q]--,q++;
    }
    int main(){
        scanf("%d",&n);
        for(int i=1;i<=n;++i){
            scanf("%d%d",&l,&r);
            a[l]=r;
        }
        solve(1,n);
        cout<<ans;
        return 0;
    }
  • 相关阅读:
    C语言之数据类型(int float double char unsigned )
    c语言的第一个程序
    socket之udp服务器和客户端
    页面置换算法的模拟实现 C
    C算法--入门篇(1)图形输出
    C算法--入门篇(1)查找元素
    C算法--入门篇(1)入门模拟2
    C算法--入门篇(1)入门模拟1
    C算法--黑盒测试
    C算法--复杂度
  • 原文地址:https://www.cnblogs.com/ZH-comld/p/9880982.html
Copyright © 2020-2023  润新知