BZOJ1044二分答案(第一问)+动态规划(第二问)

第一问:满足二分性质,直接二分答案算答案l

第二问:F[ i ][ j ]表示经过 i 此操作将前 j 个木棍分解的方案数(并且在第j个位置分解)

      F[ i ][ j ] = Sigma F[ i-1 ][ p ] (满足sum[ j ] - sum[ p ] <= l), sum[ i ]为前 i 个木棍的长度前缀和

      发现空间爆了,第一维滚动数组回收垃圾

      ans = Sigma F[ i ][ n ]

tips : 可以认为将木块分成m+1块,最后一个分解点在第n块木块右侧(木块最后)


#include 
#include 
#include 
#define mod 10007
#define N 50050
#define F f[i&1]
#define G f[1 - i&1]

using namespace std;

int a[N],sum[N],n,m,l,r,f[2][N],ans;

bool go(int x) {
	int tmp = 0 , tot = 0;
	for (int i=1;i<=n;i++) 
		if (tmp + a[i] > x) 
			tot++ , tmp = a[i];
		else 
			tmp += a[i];
	if (tot <= m) return true; else return false;
}

int main()
{
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++) scanf("%d",&a[i]);
	for (int i=1;i<=n;i++) sum[i] = sum[i-1] + a[i];
	l = 1 , r = sum[n];
	for (int i=1;i<=n;i++) l = max(l,a[i]);
	while (l < r) {
		int mid = (l+r) / 2;
		if (go(mid)) r = mid; else l = mid+1;
	}
	++m;
	int i = 1;
	G[0] = 1;
	
	for (int i=1;i<=m;i++) {
		memset(F,0,sizeof(F));
		int h = 0 , tmp = G[0];
		for (int j=1;j<=n;j++) {
			while (sum[j] - sum[h] > l && h <= j-1) tmp = (tmp - G[h++] + mod) % mod;
			F[j] = tmp;
			tmp = (tmp + G[j]) % mod;	
		}
		ans = (ans + F[n]) % mod;
	}
	i = m;
	printf("%d %d\n",l,ans);
	return 0;
}


你可能感兴趣的:(BZOJ1044二分答案(第一问)+动态规划(第二问))