洛谷 2511 [HAOI2008]木棍分割

题意:如题

思路:首先二分枚举最大的长度。

          dp[i][j]表示前一个木棍,切了j刀符合条件的方案数。

          dp[i][j]+=dp[k][j-1] (sum[i]-sum[k]<=ans)

          这个转移可以用前缀和维护。

         状态总共O(n*m),转移O(1)

#include
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define dep(i,a,b) for(int i=b;i>=a;i--)
using namespace std;
#define ll long long
const int N=3e5+5;
const int mod = 998244353;
int n,k,pre,now;
int dp[1100];
short int sum[50010][1010];
int a[50101],ans,L[50100];
ll rd()
{
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
bool check(int x)
{
	int sum=0,cnt=0;
	rep(i,1,n)
	{
		if(a[i]>x) return 0;
		sum+=a[i];
		if(sum>x)
		{
			sum=a[i];
			cnt++;
		}
	}
	return cnt<=k?1:0;
}
void init()
{
	n=rd();k=rd();
	rep(i,1,n) a[i]=rd();
	int l=0,r=1e9;
	while(l<=r)
	{
		int mid=(l+r)>>1;
		if(check(mid))
		{
			ans=mid;
			r=mid-1;
		}
		else l=mid+1;
	}
	printf("%d ",ans);
	int pre,sum=0;
	pre=1;
	rep(i,1,n)
	{
		sum+=a[i];
		if(sum>ans)
		{
			while(sum>ans)	sum-=a[pre++];	
		}
		L[i]=pre;
		//printf(" %d\n",L[i]);
	}
}
void work()
{
	dp[0]=1;
	rep(i,2,n)
	{
		rep(j,0,k)
		{
			sum[i-1][j]=(sum[i-2][j]+dp[j])%10007;
		}
		if(L[i]==1) dp[0]=1;
		else dp[0]=0;
		rep(j,1,k)
		{
			if(L[i]-2<0) dp[j]=sum[i-1][j-1];
			else dp[j]=(sum[i-1][j-1]-sum[L[i]-2][j-1]+10007)%10007;
			//printf("%d ",dp[j]); 
		}
		//printf("\n");
	}
	int sum=0;
	rep(i,0,k) sum=(sum+dp[i])%10007;
	printf("%d\n",sum);
}
int main()
{
	init();
	work();
}

 

你可能感兴趣的:(洛谷,DP,DP,洛谷)