题目传送门
题目:
题目背景
公元 2000 年,根据预言家诺查丹玛斯的预言,世界就要毁灭了!!!
题目描述
为了拯救世界,小 a 和 uim 决定召唤出 kkksc03 大神和 lzn 大神。根据古籍记载,召唤出任何一位大神,都需要使用金木水火土五种五行神石来摆一个特定的大阵。而在古籍中,记载是这样的:
kkksc03 大神召唤方法:
- 金神石的块数必须是6的倍数;
- 木神石最多用9块;
- 水神石最多用5块;
- 火神石的块数必须是4的倍数;
- 土神石最多用7块。
lzn 大神召唤方法:
- 金神石的块数必须是2的倍数;
- 木神石最多用1块;
- 水神石的块数必须是8的倍数;
- 火神石的块数必须是10的倍数;
- 土神石最多用 33 块。
现在是公元 1999 年 12 月 31 日,小 a 和 uim 从00:00:00开始找,一直找到 23:0:00,终于,还是没找到神石。
不过,他们在回到家后在自家地窖里发现了一些奇怪的东西,一查古籍,哎呦妈呀,怎么不早点来呢?这里有一些混沌之石,可以通过敲击而衰变成五行神石。于是,他们拼命地敲,终于敲出了n块神石在23:59:59完成了两座大阵。
然而,kkksc03 大神和 lzn 大神确实出现了,但是由于能量不够,无法发挥神力。只有把所有用 n 块神石可能摆出的大阵都摆出来,才能给他们充满能量。这下小 a 和 uim 傻了眼了,赶快联系上了你,让你帮忙算一下,一共有多少种大阵。
输出格式
输入一个正整数 n。
输出格式
输出用 n 块混沌之石能摆出的大阵的种数。
输入输出样例
输入 #1
2
输出#2
15
说明/提示
数据范围及约定
对于全部数据,。
提示
由于现在已经是 23:59:59,所以你只有 0.5 秒时间。(小 a 与 uim 需要 0.5s 排出所有阵法)。
题解
kkk牛逼!
生成函数裸题。
依次来看召唤每一位大神所需的每种石头的情况:
金神石的块数必须是6的倍数:。
木神石最多用9块:。
水神石最多用5块:。
火神石的块数必须是4的倍数:。
土神石最多用7块:。
金神石的块数必须是2的倍数:···。
木神石最多用1块:。
水神石的块数必须是8的倍数:。
火神石的块数必须是10的倍数:。
土神石最多用3块:。
然后把他们全部乘起来,就有:
⋅········
然后再把这个式子展开,就是:
答案就是上面这个多项式项前面的系数。
通过隔板法可以求出答案为。
由于数据范围很大,所以上NTT来优化高精度乘法即可。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const int TT=998244353;
int r[263000];char n[100005];
inline int read()
{
int ret=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-f;ch=getchar();}
while(ch>='0'&&ch<='9'){ret=ret*10+ch-'0';ch=getchar();}
return ret*f;
}
inline int QP(int a,int b)
{
int ret=1,w=a;
while(b)
{
if(b&1) ret=(LL)ret*w%TT;
w=(LL)w*w%TT;b>>=1;
}
return ret;
}
inline void NTT(int* A,int limit,int type)
{
for(int i=0;i<limit;i++)
if(i<r[i])
swap(A[i],A[r[i]]);
for(int mid=1;mid<limit;mid<<=1)
{
int gn=QP(3,(TT-1)/(mid<<1));
if(type<0) gn=QP(gn,TT-2);
for(int j=0;j<limit;j+=mid<<1)
{
int g=1;
for(int k=0;k<mid;k++,g=(LL)g*gn%TT)
{
int x=A[j+k],y=(LL)g*A[j+k+mid]%TT;
A[j+k]=(x+y)%TT;
A[j+k+mid]=(x-y+TT)%TT;
}
}
}
if(type<0)
{
int inv=QP(limit,TT-2);
for(int i=0;i<=limit;i++) A[i]=(LL)A[i]*inv%TT;
}
}
struct BigInteger
{
int len,a[263000];
BigInteger(){len=0;memset(a,0,sizeof(a));}
BigInteger(char* S)
{
len=0;memset(a,0,sizeof(a));
int n=strlen(S+1);
reverse(S+1,S+1+n);
len=(n+1)/2;
for(int i=1;i<=n;i++) S[i]-='0';
for(int i=1;i<=len;i++)
a[i]=S[i*2-1]+S[i*2]*10;
}
BigInteger operator * (BigInteger b)
{
BigInteger a=*this,c;
c.len=a.len+b.len;
int limit=1,l=0;
while(limit<=c.len){limit<<=1;l++;}
for(int i=0;i<limit;i++)
r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
NTT(a.a+1,limit,1);
NTT(b.a+1,limit,1);
for(int i=1;i<=limit;i++)
c.a[i]=(LL)a.a[i]*b.a[i]%TT;
NTT(c.a+1,limit,-1);
for(int i=1;i<=c.len;i++)
{
c.a[i+1]+=c.a[i]/100;
c.a[i]%=100;
}
if(!c.a[c.len]) c.len--;
return c;
}
void operator /= (int b)
{
for(int i=len;i;i--)
{
a[i-1]+=(a[i]%b)*100;
a[i]/=b;
}
a[0]=0;
if(!a[len]) len--;
}
inline void Inc()
{
a[1]++;
for(int i=1;i<=len;i++)
{
if(a[i]<100) break;
a[i+1]+=a[i]/100;
a[i]%=100;
}
if(a[len+1]) len++;
}
void Print()
{
printf("%d",a[len]);
for(int i=len-1;i>0;i--)
printf("%02d",a[i]);
putchar('\n');
}
}A,B,C,D,ans;
int main()
{
scanf("%s",n+1);
A=n;A.Inc();
B=A;B.Inc();
C=B;C.Inc();
D=C;C.Inc();
ans=A*B*C*D;
ans/=24;
ans.Print();
return 0;
}
以上的题目和公式都是博主手打的(时间:三天三夜),看在博主这么辛苦的份上给个点赞加关注吧,求求了!!!
另外,由于博主时间不够,格式可能不太好看,后续会慢慢优化。