[Topcoder 11213] Apple Trees

题目大意

数轴上有D个连续整数刻度,有N棵树要种在这些刻度上,其中第i棵与两旁(如果有的话)相邻的树至少要相距Ri,问方法数。
\(1\le D\le 10^5,1\le N,R_i\le 40\)

解析

我们首先考虑如何去掉 \(D\) 对复杂度的影响。假如种树的顺序确定为一个排列 \(p_{1...n}\) ,那么我们只要满足第 \(i\) 棵树和第 \(i+1\) 棵树之间的距离大于等于 \(\max (R_{p_i},R_{p_{i+1}})\) 即可,剩下的刻度相当于要放到 \(n+1\) 个空位里,插板法就能做了。

假设 \(L=\sum_{i=1}^{n}\max (R_{p_i},R_{p_{i+1}})\) ,那么我们对每一个 \(L\) ,都会剩下 \(D-(L-n+1)-n\) 个刻度需要插板。我们不妨对每一个 \(L\) DP得到有多少个排列能够计算得到 \(L\) 。我们先按照 \(R\) 从小到大排序,设 \(f_{i,j,k}\) 表示当前放了前\(i\) 个数,构成了 \(j\) 个连续段,此时的总价值为 \(k\) 的方案数。转移一共有三种情况:

  • 新加进来的数单独构成一个连续段;
  • 新加进来的数将某两个连续段连成了一个;
  • 新的数加在某个连续段的前面或者后面。

三种情况的转移系数都比较好搞。最后要求的就是 \(f_{n,1,L}\)

代码

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#define N 42
#define M 200002
using namespace std;
const int mod=1000000007;
int r[N],f[N][N][N*N],fac[M],inv[M];
int poww(int a,int b)
{
	int ans=1,base=a;
	while(b){
		if(b&1) ans=1LL*ans*base%mod;
		base=1LL*base*base%mod;
		b>>=1;
	}
	return ans;
}
int C(int n,int m)
{
	if(n<0||m<0||n<m) return 0;
	return 1LL*fac[n]*inv[m]%mod*inv[n-m]%mod;
}
class AppleTrees{
public:
	int theCount(int d,vector<int> a)
	{
		int sum=0,n=0,m,ans=0;
		n=a.size();
		for(int i=0;i<n;i++) r[i+1]=a[i];
		sort(r+1,r+n+1);
		for(int i=1;i<=n;i++) sum=max(sum,r[i]);
		sum=min(n*sum,d);m=max(sum,d);
		f[0][0][0]=fac[0]=1;
		for(int i=1;i<=m+n;i++) fac[i]=1LL*fac[i-1]*i%mod;
		inv[m+n]=poww(fac[m+n],mod-2);
		for(int i=m+n-1;i>=0;i--) inv[i]=1LL*inv[i+1]*(i+1)%mod;
		for(int i=0;i<n;i++){
			for(int j=0;j<=i;j++){
				for(int k=0;k<=sum;k++){
					f[i+1][j+1][k]=(f[i+1][j+1][k]+f[i][j][k])%mod;
					f[i+1][j-1][k+2*r[i+1]]=(f[i+1][j-1][k+2*r[i+1]]+2LL*C(j,2)*f[i][j][k]%mod)%mod;
					f[i+1][j][k+r[i+1]]=(f[i+1][j][k+r[i+1]]+2LL*j*f[i][j][k]%mod)%mod;
				}
			}
		}
		for(int i=0;i<=sum;i++) ans=(ans+1LL*f[n][1][i]*C(d-i+n-1,n)%mod)%mod;
		return ans;
	}
};
posted @ 2020-11-12 22:24  CJlzf  阅读(223)  评论(0编辑  收藏  举报