bzoj 1044 [HAOI2008]木棍分割——前缀和优化dp

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1044

前缀和优化。

但开成long long会T。(仔细一看不用开long long)

#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;
const int N=5e4+5,M=1005;
const ll mod=10007;
int n,m,a[N],pos[N];
ll dp[N],l,r,lm,s[N],c[N],ans;
void init()
{
    while(l<=r)
    {
        ll mid=((l+r)>>1),sum=0;int cnt=1;
        for(int i=1;i<=n;i++)
        {
            if(sum+a[i]>mid)
            {
                sum=a[i];cnt++;
            }
            else sum+=a[i];
        }
        if(cnt-1>m)l=mid+1;//分段,故cnt-1 
        else lm=mid,r=mid-1;
    }
    printf("%lld ",lm);//
}
void pre()
{
    for(int i=1;i<=n;i++)
    {
        if(s[i]>lm)break;dp[i]=1;//dp:把前i个分成_段的方案数 
    }
    int now=0;
    for(int i=1;i<=n;i++)
        if(s[i]>lm)
        {
            while(s[i]-s[now]>lm)now++;
            pos[i]=now;//pos:本段首个的前一个 
        }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]),l=max(l,(long long)a[i]),s[i]=s[i-1]+a[i];r=s[n];
    init();pre();
    while(m--)//已有分成1段的方案数,再来m次 
    {
        for(int i=1;i<=n;i++)c[i]=(c[i-1]+dp[i])%mod;//c:dp的前缀和 
        for(int i=1;i<=n;i++)dp[i]=((c[i-1]-c[max(0,pos[i]-1)])%mod+mod)%mod;//pos[i]-1:c[pos[i]]符合 
        (ans+=dp[n])%=mod;
    }
    printf("%lld",ans);
    return 0;
}
TLE的long long

而且如果输出的 lm 改成 lm%mod ,就会WA。只开int就都好啦。(为什么?)

那个处理pos的地方很好。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=5e4+5,mod=10007;
int n,m,a[N],pos[N],dp[N],l,r,lm,s[N],c[N],ans;
void init()
{
    while(l<=r)
    {
        int mid=((l+r)>>1),sum=0,cnt=1;
        for(int i=1;i<=n;i++)
        {
            if(sum+a[i]>mid)
            {
                sum=a[i];cnt++;
            }
            else sum+=a[i];
        }
        if(cnt-1>m)l=mid+1;
        else lm=mid,r=mid-1;
    }
    printf("%d ",lm);
}
void pre()
{
    for(int i=1;i<=n;i++)
    {
        if(s[i]>lm)break;dp[i]=1;//dp:把前i个分成_段的方案数 
    }
    int now=0;
    for(int i=1;i<=n;i++)
        if(s[i]>lm)
        {
            while(s[i]-s[now]>lm)now++;
            pos[i]=now;//pos:本段首个的前一个 
        }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]),l=max(l,a[i]),s[i]=s[i-1]+a[i];r=s[n];
    init();pre();
    while(m--)//已有分成1段的方案数,再来m次 
    {
        for(int i=1;i<=n;i++)c[i]=(c[i-1]+dp[i])%mod;//c:dp的前缀和 
        for(int i=1;i<=n;i++)dp[i]=((c[i-1]-c[max(0,pos[i]-1)])%mod+mod)%mod;//pos[i]-1:c[pos[i]]符合 
        (ans+=dp[n])%=mod;
    }
    printf("%d",ans);
    return 0;
}

 

posted on 2018-06-29 15:27  Narh  阅读(139)  评论(0编辑  收藏  举报

导航