zoukankan      html  css  js  c++  java
  • [BZOJ1044][HAOI2008]木棍分割 二分 + 单调队列优化dp + 滚动数组优化dp

    Description

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

    Input

      输入文件第一行有2个数n,m.接下来n行每行一个正整数Li,表示第i根木棍的长度.n<=50000,0<=m<=min(n-1,10
    00),1<=Li<=1000.

    Output

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

    Sample Input

    3 2 

    1
    10

    Sample Output

    10 2

    HINT

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

    Solution

    做法:二分+单调队列优化+滚动数组优化

    第一问直接二分答案然后暴力$check$一下就行了,套路

    第二问的话呢,用$dp$来求出答案

    可以设$f[i][j]$表示前$i$个木棍切成$j$段时最大长度不大于第一问的$ans$的方案数

    那么$$f[i][j]=sum_{k}^{k<=i}f[k][j-1](sum[i]-sum[k-1]>=ans1)$$

    那个$sum$前缀和一下就可以了

    然而$dp$空间爆炸,但是可以发现切成$j$个时只会从$j-1$转移过来,所以滚动掉$j$这一维

    然后就会发现时间爆炸,不过可以发现对于每个$i$,转移的$k$是固定的,所以拿个单调队列扫一扫就行了

    #include <bits/stdc++.h>
    
    using namespace std ;
    
    #define N 100010
    #define inf 0x3f3f3f3f
    #define mod 10007
    
    int n , m ;
    int a[ N ] , f[ 2 ][ N ] , q[ N ] , sum[ N ] ;
    
    bool check( int x ) {
        int sum = 0 , cnt = 0 ;
        for( int i = 1 ; i <= n ; i ++ ) {
            if( sum + a[ i ] > x ) sum = 0 , cnt ++ ;
            sum += a[ i ] ;
            if( a[ i ] > x ) return 0 ;
            if( cnt > m ) return 0 ;
        }
        return 1 ;
    }
    
    int main() {
        scanf( "%d%d" , &n , &m ) ;
        int l = inf , r , ans ;
        for( int i = 1 ; i <= n ; i ++ ) {
            scanf( "%d" , &a[ i ] ) ;
            l = min( l , a[ i ] ) ;
            sum[ i ] = sum[ i - 1 ] + a[ i ] ;
        }
        r = sum[ n ] ;
        while( l <= r ) {
            int mid = ( l + r ) >> 1 ;
            if( check( mid ) ) ans = mid , r = mid - 1 ;
            else l = mid + 1 ;
        }
        printf( "%d " , ans ) ;
        f[ 0 ][ 0 ] = 1 ;
        int ans2 = 0 ;
        for( int i = 1 ; i <= m ; i ++ ) {
            int pre = i&1 , cur = pre^1 , tot = f[ cur ][ 0 ] ;
            l = 1 , r = 1 ;
            q[ 1 ] = 0 ;
            for( int j = 1 ; j <= n ; j ++ ) {
                while( l <= r && sum[ j ] - sum[ q[ l ] ] > ans ) 
                    tot = ( tot - f[ cur ][ q[ l ++ ] ] + mod ) % mod ;
                f[ pre ][ j ] = tot ;
                q[ ++ r ] = j ;
                tot = ( tot + f[ cur ][ j ] + mod ) % mod ;
            }
            for( int j = n - 1 ; j ; j -- ) {
                if( sum[ n ] - sum[ j ] > ans ) break ;
                ans2 = ( ans2 + f[ pre ][ j ] + mod ) % mod ;
            }
            memset( f[ cur ] , 0 , sizeof( f[ cur ] ) ) ;
        }
        printf( "%d
    " , ans2 ) ;
        return 0 ;
    }
  • 相关阅读:
    【k8s】pv 处在 Terminating 状态
    【k8s】命令行自动补全
    【k8s】允许 master 节点运行 pod
    【k8s】Harbor 安装
    Nginx 允许 frame 嵌套
    Python基础教程:json中load和loads区别
    Python 基础教程:用户交互语句
    Python正则表达式-常用函数的基本使用
    Python字典循环与字典排序
    4道Python文件操作和函数练习题
  • 原文地址:https://www.cnblogs.com/henry-1202/p/9715963.html
Copyright © 2011-2022 走看看