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\n",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

 

posted @ 2018-05-23 22:46  walfy  阅读(194)  评论(0编辑  收藏  举报