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;
}
由于博主比较菜,所以有很多东西待学习,大部分文章会持续更新,另外如果有出错或者不周之处,欢迎大家在评论中指出!