题意:
给你 n 个变量 c1,c2,...,cn ,现在让你往里放任意整数,要求
- ci−1∣ci
- 1≤ci≤C
给你 n 和 C ,问你有多少种放数的方案满足上述要求。答案模 998244353 。
样例:
1 2 3 4 5 6 7 8
| input: 3 3 output: 7 input: 100000 100000 output: 502197289
|
这道题现场没敢写,首先感觉会不会是数论函数之类的题,那种我做不来。然后简单想了一下感觉像是组合数 dp ,这种我也不擅长,以前翻车过很多次,就直接弃疗了。考完看题解才发现其实我想得已经很接近了,只需要再坚持一下就能做出来。果然 ACM 最后一个小时一定不能松懈,坚持就是胜利啊 233 。
现场我想的时候总是拘泥于 C 的限制,所以不好做。也想过枚举最后一个数,但是考虑到 n 是 1e5 还是没继续想。其实枚举最后一个数之后复杂度就跟 n 无关了,可以直接公式算。
假设最后一个数为 p1q1p2q2...pkqk ,由于不同质因子之间完全独立的,所以可以一次只考虑一个质数幂因子,最后用乘法原理乘起来。
那么对于 piqi ,方案数其实就是把 qi 个质因子分配到 n 个变量里的问题。用插板法,答案就是 C(n−1,qi+n−1) 。
计算组合数可以预处理 1 到 n+m 的阶乘和阶乘的逆元,然后直接用组合数的阶乘公式算。复杂度是 O(CC) 。
注意不要测完 100000 100000 就 OK 了,记得手测边界情况(如 1 100000 )。
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| #include<iostream> #include<cstdio> using namespace std; int mo=998244353; long long n,m; long long prm[510000]; int prf[510000],prt=0; long long jie[1100000]; long long rej[1100000]; void gtp(){ for(int i=2;i<=m;++i){ if(prf[i]==0) prm[++prt]=i; for(int j=1;j<=prt && i*prm[j]<=m;++j){ prf[i*prm[j]]=1; if(i%prm[j]==0) break; } } } long long qcp(long long x,long long y){ long long z=1; for(;y;y>>=1){ if(y&1) z=z*x%mo; x=x*x%mo; } return z; } void gtj(){ jie[0]=1; rej[0]=1; for(int i=1;i<=n+m;++i){ jie[i]=jie[i-1]*i%mo; rej[i]=qcp(jie[i],mo-2); } } inline long long C(int x,int y){ return jie[y]*rej[x]%mo*rej[y-x]%mo; } inline long long ccl(int x){ return C(n-1,x+n-1); } long long run(long long x){ long long sum=1,cnt=0; for(int i=1;prm[i]*prm[i]<=x && i<=prt;++i){ cnt=0; for(;x%prm[i]==0;x/=prm[i],cnt++); if(cnt>0) sum=sum*ccl(cnt)%mo; } if(x>1) sum=sum*ccl(1)%mo; return sum; } int main(){ scanf("%lld%lld",&n,&m); gtp(); gtj(); long long ans=0; for(int i=1;i<=m;++i) ans=(ans+run(i))%mo; printf("%lld\n",ans); return 0; }
|