正题
题目链接:https://www.luogu.com.cn/problem/P2000
题目大意
十种东西,有要求
金神石A的块数必须是 6 的倍数。
木神石A最多用 9 块。
水神石A最多用 5 块。
火神石A的块数必须是 4 的倍数。
土神石A最多用 7 块。
金神石B的块数必须是 2 的倍数。
木神石B最多用 1 块。
水神石B的块数必须是 8 的倍数。
火神石B的块数必须是 10 的倍数。
土神石B最多用 3 块。
要求所有物品物件和为nnn,求方案数。
解题思路
考虑生成函数,用生成函数分别表示就是
(1+x6+x12+x18+...)∗(x1+x2+...x9)∗...(1+x^6+x^{12}+x^{18}+...)*(x^1+x^2+...x^9)*...(1+x6+x12+x18+...)∗(x1+x2+...x9)∗...
推下去,我们可以化简后得出
11−x6∗1−x101−x∗1−x61−x∗11−x4∗1−x81−x\frac{1}{1-x^6}*\frac{1-x^{10}}{1-x}*\frac{1-x^6}{1-x}*\frac{1}{1-x^4}*\frac{1-x^8}{1-x}1−x61∗1−x1−x10∗1−x1−x6∗1−x41∗1−x1−x8
∗*∗
11−x2∗1−x21−x∗11−x8∗11−x10∗11−x3\frac{1}{1-x^2}*\frac{1-x^2}{1-x}*\frac{1}{1-x^8}*\frac{1}{1-x^{10}}*\frac{1}{1-x^3}1−x21∗1−x1−x2∗1−x81∗1−x101∗1−x31
然后约分后得到
原式=1(1−x)5=Cn+44原式=\frac{1}{(1-x)^5}=C^{4}_{n+4}原式=(1−x)51=Cn+44
最后化简成组合数我是不会的,但是换种方法可以理解为
1(1−x)2=(∑i=0∞xi)5\frac{1}{(1-x)^2}=(\sum_{i=0}^{\infty}x^i)^5(1−x)21=(i=0∑∞xi)5
就是将nnn个数划分成五段的方案数(可以为空)
这样就可以化简成那个组合数
然后考虑高精度计算Cn+44=(n+1)∗(n+2)∗(n+3)∗(n+4)24C^4_{n+4}=\frac{(n+1)*(n+2)*(n+3)*(n+4)}{24}Cn+44=24(n+1)∗(n+2)∗(n+3)∗(n+4)
因为数据很大,需要NTTNTTNTT优化高精度,这里的方法是,
因为原本的乘法需要模101010,不是质数很难搞,这里我们可以先让他模一个大质数,计算完后再统一进位(这里需要保证两个位上的数相乘不会大于那个大质数)
然后除单精就好了
codecodecode
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=5e6+10,XJQ=998244353;
char s[N];
ll n,L,invn;
ll a[N],b[N],r[N];
ll power(ll x,ll b){ll ans=1;while(b){if(b&1)ans=ans*x%XJQ;x=x*x%XJQ;b>>=1;}return ans;
}
void NTT(ll *x,ll op){for(ll i=0;i<n;i++)if(i<r[i])swap(x[i],x[r[i]]);for(ll p=2;p<=n;p<<=1){ll l=p>>1,tmp=power(3,(XJQ-1)/p);if(op==-1)tmp=power(tmp,XJQ-2);for(ll k=0;k<n;k+=p){ll buf=1;for(ll i=k;i<k+l;i++){ll tt=buf*x[i+l]%XJQ;x[l+i]=(x[i]-tt+XJQ)%XJQ;x[i]=(x[i]+tt)%XJQ;buf=buf*tmp%XJQ;}}}if(op==-1)for(ll i=0;i<n;i++)x[i]=x[i]*invn%XJQ;return;
}
void mul(ll x){for(ll i=0;i<L;i++)b[L-i-1]=s[i]-'0';b[0]+=x;NTT(a,1);NTT(b,1);for(ll i=0;i<n;i++)a[i]=a[i]*b[i]%XJQ,b[i]=0;NTT(a,-1);for(ll i=0;i<n;i++){(a[i+1]+=a[i]/10)%XJQ;a[i]%=10;}return;
}
int main()
{scanf("%s",s);L=strlen(s);for(ll i=0;i<L;i++)a[L-i-1]=s[i]-'0';for(n=1;n<=L*5;n<<=1);for(ll i=0;i<n;i++)r[i]=(r[i>>1]>>1)|((i&1)?(n>>1):0);invn=power(n,XJQ-2);a[0]++;for(ll i=2;i<=4;i++)mul(i);for(ll i=n-1;i>=0;i--)a[i-1]+=a[i]%24*10,a[i]/=24;ll w=n-1;while(!a[w])w--;for(;w>=0;w--)printf("%lld",a[w]);
}