bzoj1044

二分+贪心+动态规划

第一问就是二分+贪心,和跳石头挺像的

第二问是dp,dp[i][j]表示第i次切割切到了第j段木棍,转移就是dp[i][j] = sigma(dp[i-1][k]), sum[j]-sum[k]<=ans,ans是最大长度,这里第j段木棍表示现在正在分割1-j这些木棍。很明显这样时间空间都不满足,空间滚动数组就行了,时间我们需要用类似单调队列优化一下,其实也是双指针。每次sum[j]-sum[q[l]]不满足就向前移动

初值是dp[0][i]=(sum[i] <= ans)

最终答案是sigma(dp[0->m][n])

#include<bits/stdc++.h>
using namespace std;
const int N = 50010, mod = 10007;
int n, m;
int l[N], dp[2][N], sum[N], q[N];
bool check(int mid)
{
    int tot = 0, ret = 0;
    for(int i = 1; i <= n; ++i) if(l[i] > mid) return false;
    for(int i = 1; i <= n; ++i)
    {
        if(tot + l[i] > mid) tot = l[i], ++ret;
        else tot += l[i];
    }
    return ret <= m;
}
int main()
{
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; ++i) scanf("%d", &l[i]), sum[i] = sum[i - 1] + l[i];
    int l = 0, r = sum[n] + 1, ans = 0;
    while(r - l > 1)
    {
        int mid = (l + r) >> 1;
        if(check(mid)) r = ans = mid;
        else l = mid;
    }
    int pre = 0, ret = 0;
    printf("%d ", ans);
//  dp[pre][0] = 1;
    for(int i = 1; i <= n; ++i) dp[pre][i] = (sum[i] <= ans);
    for(int i = 1; i <= m; ++i)
    {
        pre ^= 1;
//      memset(dp[pre], 0, sizeof(dp[pre]));
        int l = 1, r = 0, tot = 0;
        for(int j = 1; j <= n; ++j)
        {
            while(sum[j] - sum[q[l]] > ans && l <= r) tot = ((tot - dp[pre ^ 1][q[l]]) % mod + mod) % mod, ++l;
            dp[pre][j] = tot;
            tot = ((tot + dp[pre ^ 1][j]) % mod + mod) % mod;
            q[++r] = j;
        }
        ret = (ret + dp[pre][n]) % mod;
    }
    printf("%d\n", ret);
    return 0;
}

时间: 2024-10-13 12:36:29

bzoj1044的相关文章

BZOJ1044: [HAOI2008]木棍分割

1044: [HAOI2008]木棍分割 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 1580  Solved: 567[Submit][Status] Description 有n根木棍, 第i根木棍的长度为Li,n根木棍依次连结了一起, 总共有n-1个连接处. 现在允许你最多砍断m个连接处, 砍完后n根木棍被分成了很多段,要求满足总长度最大的一段长度最小, 并且输出有多少种砍的方法使得总长度最大的一段长度最小. 并将结果mod 10007.

bzoj1044题解

[题意分析] 本题等价于如下描述: 有一个长度为n的正整数序列,要求将其分解成m+1个子串,使最大子串和最小.求这个最大子串和及对应的分解方案数. [解题思路] 第一问二分+贪心即可.容易证明对于确定的最大子串和,分解子串使子串个数最小是一个具有最优子结构的问题.复杂度O(nlog2Σli). 第二问DP即可.先预处理前缀和Si=∑lj(j∈[1,i]). 然后考虑状态:f[i][j]表示前i个元素分解成j个子串的合法方案数. 易得转移方程:f[i][j]=Σf[k][j-1](k∈[j-1,i

【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个数,

【bzoj1044】【HAOI2008】【木棍分割】

1044: [HAOI2008]木棍分割 Time Limit: 10 Sec Memory Limit: 162 MB Submit: 2018 Solved: 730 [Submit][Status][Discuss] Description 有n根木棍, 第i根木棍的长度为Li,n根木棍依次连结了一起, 总共有n-1个连接处. 现在允许你最多砍断m个连接处, 砍完后n根木棍被分成了很多段,要求满足总长度最大的一段长度最小, 并且输出有多少种砍的方法使得总长度最大的一段长度最小. 并将结果m

【BZOJ1044】【HAOI2008】木棍分割 二分+动规

转载请注明出处:http://blog.csdn.net/vmurder/article/details/42921155 第一问裸二分,第二问乱搞. f[i][j]表示用掉i次机会,到j时合法的方案数. 代码: #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define N 50100 #define mod 10007 #define inf

bzoj1044[HAOI2008]木棍分割 单调队列优化dp

1044: [HAOI2008]木棍分割 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 4314  Solved: 1664[Submit][Status][Discuss] Description 有n根木棍, 第i根木棍的长度为Li,n根木棍依次连结了一起, 总共有n-1个连接处. 现在允许你最多砍断m个连接处, 砍完后n根木棍被分成了很多段,要求满足总长度最大的一段长度最小, 并且输出有多少种砍的方法使得总长度最大的一段长度最小. 并将结果

bzoj1044 [HAOI2008]木棍分割——前缀和优化DP

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1044 咳咳...终于A了... 居然没注意到正着找pos是n方会TLE...所以要倒着找pos: 二分还写错了,改了半天... 小心前缀和取模后相减变成负数!!!!!!!!! 代码如下: #include<iostream> #include<cstdio> #include<cstring> using namespace std; int const maxn

[bzoj1044]木棍分割

第一个问题可以用贪心+二分解决第二个问题用f[i][j]表示i次分割后分割到j且满足条件的方案数,$f[i][j]=\sum_{k<j且sum[j]-sum[k]<=ans}f[i-1][k]$优化时间:前缀和优化,二分要先预处理出来(也可以用优先队列)优化空间:发现f并没有什么用处,只需要记录前缀和数组,再对其滚动即可 1 #include<bits/stdc++.h> 2 using namespace std; 3 #define mod 10007 4 int n,m,an

HAOI2008题解

又来写题解辣-然而并不太清楚题目排列情况...不管辣先写起来- T1:[bzoj1041] 题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1041 一个赤果果的数学题--- 首先坐标轴上一定有四个, 最大公约数搞一下然后判断一下就可以啦,细节全部都在代码- 代码如下: 1 var i,j,ans,d:longint; 2 t,r,m:int64; 3 function flag(x,y:longint):longint; 4 var t