zoukankan      html  css  js  c++  java
  • BZOJ 1044 木棍分割

    Description

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

    Input

    输入文件第一行有2个数n,m. 接下来n行每行一个正整数Li,表示第i根木棍的长度.

    Output

    输出有2个数, 第一个数是总长度最大的一段的长度最小值, 第二个数是有多少种砍的方法使得满足条件.

    Sample Input

    3 2
    1
    1
    10

    Sample Output

    10 2

    HINT

    两种砍的方法: (1)(1)(10)和(1 1)(10)

    数据范围  

       n<=50000, 0<=m<=min(n-1,1000).

       1<=Li<=1000.

    Source

    本题第一问明显可以二分答案(满足可二分性)——二分答案ans贪心检测。

    至于第二问,可以用序列dp来解决。f[i][j]表示前i段木棍,分成j份的合法方案数。转移也很好想,f[i][j]=Σf[k][j-1](pre[i]-pre[k]<=ans),pre代表前缀和。看上去复杂度是O(m*n2)。但其实可以优化一下,因为i↑,所有k的下界也↑。所以我们可以利用单调队列,记录对于f[i][j]所有合法的f[k][j-1]的和,以此进行dp。复杂度O(m*n)。

     1 #include<cstdio>
     2 #include<cstdlib>
     3 using namespace std;
     4 
     5 #define rhl 10007
     6 #define maxn 50010
     7 #define maxm 1010
     8 
     9 int n,m,ans,L[maxn],sum[maxn];
    10 short f[maxn][maxm],res[maxn];
    11 
    12 struct node
    13 {
    14     int a[maxn],*h,*t;
    15     node() {h = t = a;}
    16     
    17     inline void push(int k)
    18     {
    19         *(++t) = k;
    20         for (int i = 1;i <= m;++i)
    21             (res[i] += f[k][i-1]) %= rhl;
    22     }
    23 
    24     inline void pop(int k)
    25     {
    26         while (sum[k]-sum[*(h+1)] > ans)
    27         {
    28             for (int i = 1;i <= m;++i)
    29                 (res[i] -= f[*(h+1)][i-1]) %= rhl;
    30             ++h;
    31         }
    32     }
    33 }team;
    34 
    35 inline bool okay(int l)
    36 {
    37     int i,j,tot = 0;
    38     for (i = 1;i <= n;)
    39     {
    40         if (tot > m + 1) return false;
    41         for (j = i-1;j+1 <= n&&sum[j+1]-sum[i-1] <= l;++j);
    42         if (j < i) return false;
    43         ++tot; i = j+1;
    44     }
    45     return tot <= m + 1;
    46 }
    47 
    48 inline void dp()
    49 {
    50     ++m;
    51     f[0][0] = 1;
    52     team.push(0);
    53     for (int i = 1;i <= n;++i)
    54     {
    55         team.pop(i);
    56         for (int j = 1;j <= m;++j) f[i][j] = res[j];
    57         team.push(i);
    58     }
    59     ans = 0;
    60     for (int i = 1;i <= m;++i) (ans += f[n][i])%=rhl;
    61     ans = ans % rhl + rhl;
    62     ans %= rhl;
    63 }
    64 
    65 int main()
    66 {
    67     scanf("%d %d",&n,&m);
    68     for (int i = 1;i <= n;++i) scanf("%d",L+i),sum[i] = sum[i-1] + L[i];
    69     int l = 1,r = sum[n],mid;
    70     while (l <= r)
    71     {
    72         mid = (l + r) >> 1;
    73         if (!okay(mid)) l = mid + 1;
    74         else r = mid - 1;
    75     }
    76     printf("%d ",ans = l);
    77     dp();
    78     printf("%d",ans);
    79     return 0;
    80 }
    View Code
  • 相关阅读:
    二维数组排序
    正则验证
    yii2视频教材
    yii2数据库简单操作
    MySQL(zip版)安装教程
    OpenCV插件
    机器视觉项目总结——光源
    VS2017运行YOLOv4
    VS打开cmd(直接在项目路径)
    【转载】Win10安装Ubuntu子系统
  • 原文地址:https://www.cnblogs.com/mmlz/p/4293794.html
Copyright © 2011-2022 走看看