2019icpc上海网络赛D Counting Sequences I

https://nanti.jisuanke.com/t/41412
题意:给定n,求满足下面关系的集合 a a a个数。
2019icpc上海网络赛D Counting Sequences I_第1张图片
思路:容易想到,积增加的速度远快于和,那么n大一点时,就是【若干个数与若干个1相加】与【这些数相乘】相平衡。由于 2 12 = 4096 2^{12}=4096 212=4096,故非1的数最多11个,那么dfs枚举所有的组合加上剪枝即可。计算组合时,用了一点组合数学的技巧。比如1111111111223334这个,会多次枚举223334这6个数,因为 6 ! = = 这 6 个 数 的 不 同 排 列 数 ∗ 3 ! ∗ 2 ! ∗ 1 ! 6!==这6个数的不同排列数*3!*2!*1! 6!==6321,那么我们每次枚举到一个排列,都除去1的个数的阶乘,再除以6!(因为除以6!等价于除以(这6个数的不同排列数*3!*2!*1!),而这6个数的不同排列都会枚举到)。这样的话, 计 算 1 次 16 ! 10 ! ∗ 3 ! ∗ 2 ! ∗ 1 ! 就 等 效 成 计 算 6 ! 3 ! ∗ 2 ! ∗ 1 ! 次 16 ! 10 ! ∗ 6 ! 之 和 。 计算1次\frac{16!}{10!*3!*2!*1!}就等效成计算\frac{6!}{3!*2!*1!}次\frac{16!}{10!*6!}之和。 110!3!2!1!16!3!2!1!6!10!6!16!

#include
using namespace std;
const int maxn=3005;
const int mod=1000000007;
typedef long long ll;

int T,n;
ll fac[maxn],inv[maxn],ans[maxn];

ll pow_mod(ll a,ll n)
{
	if(!n)return 1;
	ll x=pow_mod(a,n/2);
	x=x*x%mod;
	if(n&1)x=x*a%mod;
	return x;
}

void init()
{
	fac[0]=inv[0]=1;
	for(int i=1;i<maxn;i++)fac[i]=fac[i-1]*i%mod,inv[i]=pow_mod(fac[i],mod-2);
}

void dfs(int depth,int sum,ll mul)
{
	int n=mul-sum+depth;
	if(n>3000)return;
	ans[n]=(ans[n]+fac[n]*inv[n-depth]%mod*inv[depth]%mod)%mod;
	for(int i=2;i<=6000/mul;i++)
	{
		dfs(depth+1,sum+i,mul*i);
	}
}

int main()
{
	//freopen("input.in","r",stdin);
	init();
	dfs(0,0,1);
	cin>>T;
	while(T--)
	{
		cin>>n;
		cout<<ans[n]<<endl;
	}
}

你可能感兴趣的:(搜索,组合数学)