拯救世界
不能一次 DFT 所有序列再一起乘起来,会爆模数的。。。
正确做法是 DFT 后卷积一次,再 IDFT 一次,并进位,这样做三次。。。
在学校机房里看的。
比较适合作为生成函数的入门例题。
但是要写高精度,在学校里还是算了。
只讲一下思路。
根据题目所给的十个限制,我们其实可以列出十个生成函数:
$F_1(x)=\sum_{n\ge 0}x^{6n}=\dfrac{1}{1-x^6}$
$F_2(x)=\sum_{n=0}^9x^n=\dfrac{1-x^{10}}{1-x}$
$F_3(x)=\sum_{n=0}^5x^n=\dfrac{1-x^6}{1-x}$
$F_4(x)=\sum_{n\ge 0}x^{4n}=\dfrac{1}{1-x^4}$
$F_5(x)=\sum_{n=0}^7x^n=\dfrac{1-x^8}{1-x}$
$G_1(x)=\sum_{n\ge 0}x^{2n}=\dfrac{1}{1-x^2}$
$G_2(x)=\sum_{n=0}^1x^n=\dfrac{1-x^2}{1-x}$
$G_3(x)=\sum_{n\ge0}x^{8n}=\dfrac{1}{1-x^8}$
$G_4(x)=\sum_{n\ge 0}x^{10n}=\dfrac{1}{1-x^{10}}$
$G_5(x)=\sum_{n=0}^3x^n=\dfrac{1-x^4}{1-x}$
然后这个题的组合意义是什么?
用 $n$ 块且满足条件,显然把 10 个生成函数乘起来,对于次数为 $n$ 的项的系数就是答案(实质就是背包,不如说背包的实质其实是生成函数的卷积)。
不多做解释。
然后我们发现这个题最后乘下来的答案非常漂亮:
$H(x)=\dfrac{1}{(1-x)^5}$
这玩意是什么?
熟悉的人一眼就可以看出来,不做赘述。
$H(x)=\sum_{n\ge 0}\dbinom{n+4}{n}x^n$
第 $n$ 项的系数就是 $\dbinom{n+4}{n}=\dfrac{(n+1)(n+2)(n+3)(n+4)}{24}$,就是答案。
但是这个题要高精度。还要用 NTT。
时间复杂度 $O(\log n\log \log n)$。
代码:
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 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
| #include<iostream> #include<cstdio> #include<cstring> #define ll long long using namespace std;
const ll N=4e6,mo=2281701377ll,G=3ll;
namespace Aeft{ inline ll Pow(ll b,ll p) { ll res=1;while(p) {if(p&1) res=res*b%mo;b=b*b%mo;p>>=1;}return res; } }using Aeft::Pow;
const ll invG=Pow(G,mo-2);
ll n,m,invn; ll a[N+5],b[N+5],c[N+5],d[N+5],rev[N+5]; char s[N+5];
inline void NTT(ll *f,bool op) { for(ll i=0;i<n;i++) if(i<rev[i]) swap(f[i],f[rev[i]]); for(ll p=2;p<=n;p<<=1) { ll len=p>>1,tG=Pow(op?invG:G,(mo-1)/p); for(ll k=0;k<n;k+=p) { for(ll l=k,buf=1;l<k+len;l++,buf=buf*tG%mo) { ll t=buf*f[len+l]%mo; f[len+l]=f[l]-t;if(f[len+l]<0) f[len+l]+=mo; f[l]=f[l]+t;if(f[l]>mo) f[l]-=mo; } } } if(op) {for(ll i=0;i<n;i++) f[i]=f[i]*invn%mo;} }
inline void Plus_(ll *a,ll x,ll *b) { static ll buf[N+5]; for(ll i=0;i<n;i++) buf[i]=a[i];buf[0]+=x; for(ll i=0;i<n;i++) {buf[i+1]+=buf[i]/10ll;buf[i]%=10ll;} for(ll i=0;i<n;i++) b[i]=buf[i]; }
inline void Div_(ll *a,ll x) { for(ll i=n-1;i>=0;i--) {a[i-1]+=(a[i]%x)*10ll;a[i]/=x;} }
inline void Karry(ll *a) { for(ll i=0;i<n;i++) {a[i+1]+=a[i]/10ll;a[i]%=10ll;} }
int main() {
scanf("%s",s);n=strlen(s); for(ll i=0;i<n;i++) {if(s[i]>=48&&s[i]<=57) a[n-i-1]=s[i]-48ll;} for(m=n*4,n=1;n<=m;n<<=1);invn=Pow(n,mo-2); for(ll i=0;i<n;i++) rev[i]=(rev[i>>1]>>1)|((i&1)?(n>>1):0);
Plus_(a,1,a);Plus_(a,1,b);Plus_(b,1,c);Plus_(c,1,d);
NTT(a,0);NTT(b,0); for(ll i=0;i<n;i++) {a[i]=a[i]*b[i]%mo;} NTT(a,1);Karry(a);NTT(a,0); NTT(c,0); for(ll i=0;i<n;i++) {a[i]=a[i]*c[i]%mo;} NTT(a,1);Karry(a);NTT(a,0); NTT(d,0); for(ll i=0;i<n;i++) {a[i]=a[i]*d[i]%mo;} NTT(a,1);Karry(a);Div_(a,24ll);Karry(a);
ll flg=0; for(ll i=n-1;i>=0;i--) { if(a[i]==0&&!flg) continue; else {flg=1;putchar(a[i]+48ll);} }
return 0; }
|