zoukankan      html  css  js  c++  java
  • bzoj1044: [HAOI2008]木棍分割 二分+dp

    有n根木棍, 第i根木棍的长度为Li,n根木棍依次连结了一起, 总共有n-1个连接处. 现在允许你最多砍断m个连接处, 砍完后n根木棍被分成了很多段,要求满足总长度最大的一段长度最小, 并且输出有多少种砍的方法使得总长度最大的一段长度最小. 并将结果mod 10007。

    题解:先二分求最大长度的最小值,贪心的切看能不能满足条件,然后dp【i】【j】表示切了i刀,切到j的满足条件的方案数,然后复杂度是O(n*n*m),我们可以发现,转移是从一段连续的区间转移的,所以我们每次求上一层dp的前缀和来加速,复杂度就成了O(n*m),然后开二维dp会爆空间,我们需要滚动数组压缩空间

    /**************************************************************
        Problem: 1044
        User: walfy
        Language: C++
        Result: Accepted
        Time:8564 ms
        Memory:2264 kb
    ****************************************************************/
     
    //#pragma comment(linker, "/stack:200000000")
    //#pragma GCC optimize("Ofast,no-stack-protector")
    //#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,tune=native")
    //#pragma GCC optimize("unroll-loops")
    #include<bits/stdc++.h>
    #define fi first
    #define se second
    #define mp make_pair
    #define pb push_back
    #define pi acos(-1.0)
    #define ll long long
    #define vi vector<int>
    #define mod 10007
    #define C 0.5772156649
    #define ls l,m,rt<<1
    #define rs m+1,r,rt<<1|1
    #define pil pair<int,ll>
    #define pli pair<ll,int>
    #define pii pair<int,int>
    #define cd complex<double>
    #define ull unsigned long long
    #define base 1000000000000000000
    #define fio ios::sync_with_stdio(false);cin.tie(0)
     
    using namespace std;
     
    const double eps=1e-6;
    const int N=50000+10,maxn=5000+10,inf=0x3f3f3f3f,INF=0x3f3f3f3f3f3f3f3f;
     
    int ans,dp[2][N];
    int a[N],sum[N],n,m,last[N];
    bool ok(int x)
    {
        int now=0,te=0;
        for(int i=1;i<=n;i++)
        {
            if(now+a[i]<=x)now+=a[i];
            else
            {
                te++;
                now=a[i];
                if(now>x)return 0;
            }
        }
        if(te>m)return 0;
        return 1;
    }
    void prepare()
    {
        int l=0,r=sum[n]+1;
        while(l<r-1)
        {
            int m=(l+r)>>1;
            if(ok(m))r=m;
            else l=m;
        }
        ans=r;
        for(int i=1;i<=n;i++)a[i]+=a[i-1];
    }
    void gao()
    {
        int now=0,pre=1,ans1=0;
        dp[now][0]=1;
        for(int i=1;i<=m+1;i++)
        {
            swap(now,pre);
            memset(dp[now],0,sizeof dp[now]);
            sum[0]=dp[pre][0];
            for(int j=1;j<=n;j++)sum[j]=(sum[j-1]+dp[pre][j])%mod;
            int k=0;
            dp[now][0]=0;
            for(int j=i;j<=n;j++)
            {
                while(a[j]-a[k]>ans)k++;
                dp[now][j]=(sum[j-1]-(k?sum[k-1]:0))%mod;
                dp[now][j]=(dp[now][j]+mod)%mod;
            }
            ans1=(ans1+dp[now][n])%mod;
        }
        printf("%d %d
    ",ans,(ans1+mod)%mod);
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            sum[i]=sum[i-1]+a[i];
        }
        prepare();
        gao();
        return 0;
    }
    /********************
    3 2
    10 1 1
    ********************/
    View Code
  • 相关阅读:
    【2021-01-07】叫我“何工”就好
    2021年的 目标计划
    转:我在拼多多的三年 https://www.leadroyal.cn/?p=1228#more-1228
    匀强电场等效重力场
    开源图片素材管理软件-civet
    心学#传习录学习
    工作感受月记202101月
    统计字典序元音字符的数量
    可被5整除的二进制
    假期刷题--JAVA
  • 原文地址:https://www.cnblogs.com/acjiumeng/p/9080130.html
Copyright © 2011-2022 走看看