• 「长乐集训 2017 Day10」划分序列 (二分 dp)


    「长乐集训 2017 Day10」划分序列

    题目描述

    给定一个长度为 n nn 的序列 Ai A_iAi​​,现在要求把这个序列分成恰好 K KK 段,(每一段是一个连续子序列,且每个元素恰好属于一段),并且每段至少有一个元素,使得和最大的那一段最小。

    请你求出这个最小值。

    输入格式

    第一行两个整数 n,K n, Kn,K,意义见题目描述。
    接下来一行 n nn 个整数表示序列 Ai A_iAi​​。

    输出格式

    仅一行一个整数表示答案。

    样例

    样例输入

    9 4
    1 1 1 3 2 2 1 3 1

    样例输出

    5

    数据范围与提示

    20% 20 \%20% 的数据,n≤100 n leq 100n100
    另有 20% 20 \%20% 的数据,Ai≥0 A_i geq 0Ai​​0
    另有 20% 20 \%20% 的数据,Ai≤0 A_i leq 0Ai​​0
    另有 20% 20 \%20% 的数据,K≤10 K leq 10K10
    100% 100 \%100% 的数据,1≤K≤n≤5×104,∣Ai∣≤3×104 1 leq K leq n leq 5 imes 10 ^ 4, |A_i| leq 3 imes 10 ^ 41Kn5×104​​,Ai​​3×104​​

    【题意】给一个序列  有正有负  要求恰好分成k份,使得和最大的那一份最小。

    【分析】考虑二分答案,然后对于这个答案判断他的可行性。如果一段序列最大的那份小于等于x,且最少可分为 L 份,最多可分为 R 份,

     则他一定能分为res份,L<=res<=R,那我们dp求L,R,对于序列中某一段可表示为,sum[i]-sum[j]<=mid,sum[j]>=sum[i]-mid,那么dp[i]可由

     dp[j]得到,j<i且sum[j]>=sum[i]-mid,那么我们可以树状数组维护最小/最大值。

    #include <bits/stdc++.h>
    #define inf 0x3f3f3f3f
    #define met(a,b) memset(a,b,sizeof a)
    #define pb push_back
    #define mp make_pair
    #define rep(i,l,r) for(int i=(l);i<=(r);++i)
    #define inf 0x3f3f3f3f
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    const int N = 5e4+50;;
    const int M = 255;
    const int mod = 998244353;
    const int mo=123;
    const double pi= acos(-1.0);
    typedef pair<int,int>pii;
    typedef pair<ll,int>P;
    int n,k,sz;
    int a[N],mn[N],mx[N];
    int sum[N],pos[N],p[N];
    vector<pii>vec;
    void updmn(int x,int v)
    {
        for(;x;x-=x&-x)
            mn[x]=min(mn[x],v);
    }
    int getmn(int x)
    {
        int sum=inf;
        for(;x<=n;x+=x&-x)
            sum=min(sum,mn[x]);
        return sum;
    }
    void updmx(int x,int v)
    {
        for(;x;x-=x&-x)
            mx[x]=max(mx[x],v);
    }
    int getmx(int x)
    {
        int sum=-inf;
        for(;x<=n;x+=x&-x)
            sum=max(sum,mx[x]);
        return sum;
    }
    bool check(ll x){
        for(int i=0,j=0;i<vec.size();i++){
            while(j<vec.size()&&vec[j].first<vec[i].first-x)j++;
            pos[vec[i].second]=j+1;
        }
        for(int i=0;i<N;i++)mn[i]=inf,mx[i]=-inf;
        int l,r;
        ll res=0;
        for(int i=1;i<=n;i++){
            res+=a[i];
            int maxn=getmx(pos[i]);
            int minn=getmn(pos[i]);
            if(maxn!=-inf)maxn++;
            else {
                if(res<=x)maxn=1;
            }
            if(minn!=inf)minn++;
            if(res<=x)minn=1;
            updmn(p[i],minn);
            updmx(p[i],maxn);
            l=minn;r=maxn;
        }
        return l<=k&&k<=r;
    
    }
    int main(){
        scanf("%d%d",&n,&k);
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
            sum[i]=sum[i-1]+a[i];
            vec.pb(mp(sum[i],i));
        }
        sort(vec.begin(),vec.end());
        for(int i=0;i<vec.size();i++)p[vec[i].second]=i+1;
        ll l=-inf,r=inf,ans;
        while(l<=r){
            ll mid=(l+r)/2;
            if(check(mid))ans=mid,r=mid-1;
            else l=mid+1;
        }
        printf("%lld
    ",ans);
        return 0;
    }
    /*
    3 2
    -1 -2 3
    */
  • 相关阅读:
    【30篇突击 android】源码统计 二十
    【eoeAndroid社区索引】Android控件知识汇总
    【eoeAndroid社区索引】Android二维码知识汇总
    【30篇突击 android】源码统计 十八
    Andorid中xml资料汇总
    android 问题汇总系列之六
    android 问题汇总系列之七
    Android Permission大全1.0最终版本
    android中绘图的方法
    Nokia60的Image.createImage死机问题
  • 原文地址:https://www.cnblogs.com/jianrenfang/p/7624845.html
Copyright © 2020-2023  润新知