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

    题目描述

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

    输入

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

    输出

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

    样例输入

    3 2
    1
    1
    10

    样例输出

    10 2


    题解

    二分+dp

    第一问即 noip2015跳石头 。。。一眼二分,然后看不满足条件时就切一刀,判断是否小于m。

    第二问求方案数,很显然是个dp。

    设$f[i][j]$表示前$i$个分了$j$段的方案数,那么状态转移方程应该为$f[i][j]=sumlimits_{len(t+1,i)le ans1}f[t][j-1]$,边界条件$f[0][0]=1$,其中$len(a,b)表示$[a,b]$所有木棍的长度总和。

    可以发现$t$的取值范围是一段连续的单调的区间,因此可以用类似双指针的方法扫出$t$的取值左端点。然后$sum$又可以使用前缀和维护,这样时间复杂度就降为了$O(nm)$。

    然而这样还会炸空间。。。

    因此使用滚动数组就好了,显然第二维是可以滚动的,因此先枚举第二维,滚动一下就好了。

    #include <cstdio>
    #include <algorithm>
    #define N 50010
    #define mod 10007
    using namespace std;
    int n , m , a[N] , sl[N] , f[2][N] , sum[2][N];
    bool judge(int mid)
    {
    	int i , now = 0 , cnt = 0;
    	for(i = 1 ; i <= n ; i ++ )
    	{
    		if(now + a[i] > mid) now = 0 , cnt ++ ;
    		now += a[i];
    	}
    	return cnt <= m;
    }
    int main()
    {
    	int i , j , l = 0 , r = 0 , mid , ans = -1 , p = 0 , ret = 0 , d;
    	scanf("%d%d" , &n , &m);
    	for(i = 1 ; i <= n ; i ++ ) scanf("%d" , &a[i]) , l = max(l , a[i]) , r += a[i] , sl[i] = sl[i - 1] + a[i];
    	while(l <= r)
    	{
    		mid = (l + r) >> 1;
    		if(judge(mid)) ans = mid , r = mid - 1;
    		else l = mid + 1;
    	}
    	printf("%d " , ans);
    	for(i = 0 ; i <= n ; i ++ ) sum[0][i] = 1;
    	for(i = d = 1 ; i <= m + 1 ; i ++ , d ^= 1)
    	{
    		sum[d][0] = p = 0;
    		for(j = 1 ; j <= n ; j ++ )
    		{
    			while(sl[j] - sl[p] > ans) p ++ ;
    			f[d][j] = sum[d ^ 1][j - 1];
    			if(p) f[d][j] = (f[d][j] - sum[d ^ 1][p - 1] + mod) % mod;
    			sum[d][j] = (sum[d][j - 1] + f[d][j]) % mod;
    		}
    		ret = (ret + f[d][n]) % mod;
    	}
    	printf("%d
    " , ret);
    	return 0;
    }
    

     

  • 相关阅读:
    如何实现ZBrush 4R7中按钮颜色的自定义
    Zbrush遮罩边界该怎么实现羽化和锐化
    怎么在ZBrush中通过遮罩得到子物体
    怎样用好ZBrush中的PaintStop插件
    SQL 如果存在就更新,如果不存在就添加,使用 Merge 函数(SQL2008版本及以上)
    SQL 实现,如果存在就更新,如果不存在就添加
    验证码,字体旋转。
    瀑布流代码,简洁版 带分页
    瀑布流代码,简洁版
    EF
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/7491510.html
Copyright © 2011-2022 走看看