木棍分割-dp,前缀和优化

木棍分割-dp,前缀和优化

P2511 木棍分割

题意

\(n\) 根木棍,给出长度,要分成 \(m\) 段,问总长度最大的一段最小长度是多少,并求出其方案数对 \(10007\) 取模的结果。

思路

第一问很容易想到用二分,第二问也比较容易想到用 \(dp\) 。二分怎么做这里不做赘述。

考虑 \(dp\) ,我们要满足长度最小,且分出来的段数小于 \(m+1\) ,很容易设计出一维给段数,一维放长度不合适,但是可以存已经做到哪里,也就是 \(dp[i][j]\) 表示到 \(i\) 分出 \(j\) 块的方案数。

状态转移:

\[dp_{i,j} = \sum_{len_j-len_k \leq milen} dp_{i-1,k} \]

此时两层枚举加上找 \(k\) 时间复杂度 \(O(n^3)\) ,发现找 \(k\) 可以用前缀和优化,而且长度只会递增所以我们可以预处理出每个点的 \(k\) 。空间还要压一下。

code

#include <bits/stdc++.h>
#define int long long
using namespace std;
constexpr int maxn = 5e4+10;
constexpr int mod = 1e4+7;
void read(int &);

int n,m;
int li[maxn];
int dp[maxn],sum[maxn],st[maxn];
int sumdp[maxn];// 对对应dp作前缀和

inline void add(int &x,const int y)
{
    x+=y;
    if(x>=mod)
    {
        x-=mod;
    }
    else if(x<0)
    {
        x+=mod;
    }
}

bool check(int x)
{
    int cnt=0,len=0;// cnt=1-wa..
    for(int i=1;i<=n;++i)
    {
        if(li[i]>x) return 0;
        if(len+li[i]>x)
        {
            ++cnt;
            len=li[i];
        }
        else
        {
            len+=li[i];
        }
        if(cnt>m)
        {
            return 0;
        }
    }
    return 1;
}

int binary(int l,int r)
{
    int mid,ans=0;
    while(l<=r)
    {
        mid=l+((r-l)>>1);
        if(check(mid))
        {
            ans=mid;
            r=mid-1;
        }
        else
        {
            l=mid+1;
        }
    }
    return ans;
}

int _dp(int x)
{
    for(int i=1,id=1;i<=n;++i)// 预处理k
    {
        if(sum[i]<=x)
        {
            dp[i]=1;// dp[i][1]
        }
        while(id<=n && sum[i]-sum[id]>x)
        {
            ++id;
        }
        st[i]=id;
    }
    for(int i=1;i<=n;++i)// dp[i]的前缀和
    {
        add(sumdp[i],sumdp[i-1]+dp[i]);
    }

    int ret=0;
    for(int i=2;i<=m+1;++i)
    {
        for(int j=1;j<=n;++j)
        {
            dp[j]=sumdp[j-1];// 上一个dp的综合
            if(st[j]-1>=0)   // 存在
            {
                add(dp[j],-sumdp[st[j]-1]);// 减去dp_lst[st-1]
            }
        }
        for(int j=1;j<=n;++j)
        {
            sumdp[j]=0;//前缀和
            add(sumdp[j],sumdp[j-1]+dp[j]);
        }
        add(ret,dp[n]);
    }
    return ret;
}

signed main()
{
    #ifndef ONLINE_JUDGE
    freopen("cjdl.in","r",stdin);
    freopen("cjdl.out","w",stdout);
    #endif // ONLINE_JUDGE

    read(n);
    read(m);
    for(int i=1;i<=n;++i)
    {
        read(li[i]);
        sum[i]=sum[i-1]+li[i];
    }

    int len=binary(1,sum[n]);
    printf("%lld ",len);

    int ans=_dp(len);
    printf("%lld\n",ans);

    return 0;
}

inline void read(int &x)
{
    x=0;
    int f=1;
    signed c=getchar();
    while(!isdigit(c))
    {
        if(c=='-')
        {
            f=-1;
        }
        c=getchar();
    }
    while(isdigit(c))
    {
        x=(x<<1)+(x<<3)+(c^48);
        c=getchar();
    }
    x*=f;
}
posted @ 2025-11-26 20:28  玖玮  阅读(1)  评论(0)    收藏  举报