FZOJ 4109 青青草原的表彰大会

由于博主对DP的感知能力过于低下,考试的时候竟然没发现这是DP。。。

发现每个数一定是它前面一个数的倍数,所以这个数列一定是有序的,也不难发现这个数列最多有\(O(logn)\)个不同的数,所以设\(f[i][j]\)表示有\(i\)个不同的数,最后一个数是\(j\)的方案数。

所以最后有\(d\)个互不相同的数的个数\(cnt[d]=\sum f[d][j]\),然后我们考虑把这些数放到长度为\(n\)的题目要求的数列中,由组合数中隔板原理,可以知道有\(d\)种数的方案有\(cnt[d] \times \binom{k-1}{d-1}\),所以\(ans=\sum_{d=1}^{log_n} cnt[d] \times \binom{k-1}{d-1}\)

代码如下:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std;

const int N=1000009,M=1000000007;
int n,k,a[N],ans,f[25][N],fac[N],invf[N];

void init()
{
	scanf("%d %d",&n,&k);
}

void ADD(int &x,int y)
{
	x=(x+y)%M;
}

int ksm(int a,int b)
{
	int res=1;
	while(b)
	{
		if(b&1)
			res=1LL*res*a%M;
		b>>=1,a=1LL*a*a%M;
	}
	return res;
}

void work()
{
	fac[0]=1;
	int Q=N-9;
	for (int i=1;i<=Q;i++)
		fac[i]=1LL*fac[i-1]*i%M;
	invf[Q]=ksm(fac[Q],M-2);
	for (int i=Q-1;i>=0;i--)
		invf[i]=1LL*invf[i+1]*(i+1)%M;
	for (int i=1;i<=n;i++)
		f[1][i]=1;
	for (int i=1;1<<i-1<=n;i++)
		for (int j=1;j<=n>>1;j++)
			if(f[i][j])
				for (int k=j+j;k<=n;k+=j)
					ADD(f[i+1][k],f[i][j]);
	int ans=0;
	for (int i=1;1<<i-1<=n;i++)
	{
		int tmp=0;
		for (int j=1;j<=n;j++)
			ADD(tmp,f[i][j]);
		ADD(ans,(int)(1LL*tmp*fac[k-1]%M*invf[i-1]%M*invf[k-i]%M));
	}
	printf("%d\n",ans);
}

int main()
{
	init();
	work();
	return 0;
}
posted @ 2020-04-15 19:37  With_penguin  阅读(154)  评论(0编辑  收藏  举报