AtCoder Regular Contest 116 C

https://atcoder.jp/contests/arc116/tasks/arc116_c

题意:
给出2个正整数n和m
问能构造出多少个长为n的序列a,满足1<=ai<=m且a[i]%a[i-1]=0

因为a[i]%a[i-1]=0
所以这个序列是不降序列
进一步可以得出不同的数字个数不会超过(log_2 m)
(log_2 m)比较小(大约18),可以考虑从这一点出发寻找答案

假设我们已经确定好要用哪k种数
这k种数只能按从小到大的顺序排列成长为n的序列
序列个数相当于在n-1个空隙中插入k-1个隔板,将n个数分为k部分
所以是(C_{n-1}^{k-1})
那么如果我们能够计算出(sum[i])表示在1-m中选出i个数,满足后一个对前一个取模等于0 的选法数量
那么答案就等于(sum_{i=1}^{min(n,(log_2 m)+1)}{sum[i]*C_{n-1}^{i-1}})

(sum[i])怎么求?
(f[i][j])表示在1-i中选了j个数,满足后一个对前一个取模等于0且i必选的方案数
枚举i的倍数转移
(f[i*k][j+1]+=f[i][j])
最后(sum[i]=sum_{j=1}^{min(n,(log_2 m)+1)}{f[j][i]})

#include<cstdio>

const int mod=998244353;

#define N 200001

int f[N][20],sum[N];
int fac[N],inv[N]; 

int pow2(int a,int b)
{
	int c=1;
	for(;b;a=1ll*a*a%mod,b>>=1)
		if(b&1) c=1ll*c*a%mod;
	return c;
}

int C(int n,int m)
{
	return 1ll*fac[n]*inv[m]%mod*inv[n-m]%mod;
}

int main()
{
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;++i) f[i][1]=1;
	for(int i=1;i<=m;++i)
		for(int j=1;j<20;++j)
			for(int k=i+i;k<=m;k+=i)
			{
				f[k][j+1]+=f[i][j];
				if(f[k][j+1]>=mod) f[k][j+1]-=mod;
			}
	fac[0]=1;
	inv[0]=1;
	for(int i=1;i<=n;++i)
	{
		fac[i]=1ll*fac[i-1]*i%mod;
		inv[i]=pow2(fac[i],mod-2);
	}
	int p=19;
	if(p>n) p=n;
	for(int i=1;i<=p;++i)
		for(int j=1;j<=m;++j)
		{
			sum[i]+=f[j][i];
			if(sum[i]>=mod) sum[i]-=mod;
		}
	int ans=0;
	for(int i=1;i<=p;++i)
	{
		ans+=1ll*sum[i]*C(n-1,i-1)%mod;
		if(ans>=mod) ans-=mod;
	}
	printf("%d",ans);
}
作者:xxy
本文版权归作者和博客园共有,转载请用链接,请勿原文转载,Thanks♪(・ω・)ノ。
原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/14592889.html