luogu2000 拯救世界
solution
该博文刚写完,就不小心手残清空了,只好重写。
生成函数练手题
先写上这个式子\(\frac{1}{1-x}=1+x+x^2+x^3+x^4+...\)
题目中描述了两种限制。
-
第一种限制:神石的块数必须是\(t\)的倍数,那么他的生成函数就是\(1+x^t+x^{2t}+x^{3t}+...\)。这就相当于用\(x^t\)替换掉了上方式子中的\(x\)。所以该生成函数就是\(\frac{1}{1-x^t}\)
-
第二中限制:神石的数目不超过\(t\),那么他的生成函数就是\(1+x+x^2+x^3+...+x^t\)。我们将其看作\((1+x+x^2+x^3+...)-(x^{t+1}+x^{t+2}+x^{t+3}+...)\)。第一个括号里的内容就是\(\frac{1}{1-x}\),第二个括号里就相当于将最上方的式子每项都乘以\(x^{t+1}\)。所以第二个括号里的内容就是\(\frac{x^{t+1}}{1-x}\)。所以该限制的生成函数就是\(\frac{1-x^{t+1}}{1-x}\)
然后将所有的限制用生成函数描述出来,并相乘,所得多项式的第\(n\)项就是答案。
这10个限制的生成函数相乘为\(\frac{1}{1-x^6}\cdot\frac{1-x^{10}}{1-x}\cdot \frac{1-x^6}{1-x}\cdot\frac{1}{1-x^4}\cdot \frac{1-x^8}{1-x}\cdot\frac{1}{1-x^2}\cdot\frac{1-x^2}{1-x}\cdot\frac{1}{1-x^8}\cdot\frac{1}{1-x^{10}}\cdot\frac{1-x^4}{1-x}\)
约分一下发现就是\(\frac{1}{(1-x)^5}\)。
由广义二项式定理\(\frac{1}{(1-x)^k}=\sum\limits_{n\ge0}C_{n+k-1}^nx^n\)
所以第n项的系数就是\(C_{n+4}^n=\frac{(n+1)(n+2)(n+3)(n+4)}{24}\)
n比较大,用NTT或者FFT优化高精乘即可。
code
/*
* @Author: wxyww
* @Date: 2020-04-16 09:31:03
* @Last Modified time: 2020-04-16 10:17:08
*/
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
#include<ctime>
using namespace std;
typedef long long ll;
const int N = 1000010,mod = 998244353;
ll read() {
ll x = 0,f = 1;char c = getchar();
while(c < '0' || c > '9') {
if(c == '-') f = -1; c = getchar();
}
while(c >= '0' && c <= '9') {
x = x * 10 + c - '0'; c = getchar();
}
return x * f;
}
char s[N];
struct BIGNUM {
int a[N],len;
void Read() {
scanf("%s",s);
len = strlen(s);
for(int i = 0;i < len;++i) a[i] = s[len - i - 1] - '0';
}
}n,a1,a2,a3,a4;
BIGNUM operator + (BIGNUM A,int x) {
A.a[0] += x;
for(int i = 0;i < A.len;++i) {
if(A.a[i] >= 10) {
A.a[i + 1] += A.a[i] / 10;
A.a[i] %= 10;
if(i == A.len - 1) ++A.len;
}
}
return A;
}
BIGNUM operator / (BIGNUM A,int x) {
int now = 0;
for(int i = A.len - 1;i >= 0;--i) {
now = now * 10 + A.a[i];
A.a[i] = now / x;
now %= x;
}
while(!A.a[A.len - 1]) --A.len;
return A;
}
int qm(int x,int y) {
int ret = 1;
for(;y;y >>= 1,x = 1ll * x * x % mod)
if(y & 1) ret = 1ll * ret * x % mod;
return ret;
}
int rev[N];
void NTT(int *a,int n,int xs) {
for(int i = 0;i <= n;++i) if(rev[i] > i) swap(a[i],a[rev[i]]);
for(int m = 2;m <= n;m <<= 1) {
ll w1 = qm(3,(mod - 1) / m);
if(xs == -1) w1 = qm(w1,mod - 2);
for(int i = 0;i < n;i += m) {
ll w = 1;
for(int k = 0;k < (m >> 1);++k) {
ll u = a[i + k],t = w * a[i + k + (m >> 1)] % mod;
a[i + k] = (u + t) % mod;a[i + k + (m >> 1)] = (u - t + mod) % mod;
w = w * w1 % mod;
}
}
}
}
int t1[N],t2[N];
BIGNUM operator * (const BIGNUM &A,const BIGNUM &B) {
int len = A.len + B.len - 1;
int tot = 1;
while(tot <= len) tot <<= 1;
// printf("!!!%d\n",tot);
for(int i = 0;i <= tot;++i)
rev[i] = (rev[i >> 1] >> 1) | (i & 1 ? (tot >> 1) : 0);
memset(t1,0,sizeof(t1));memset(t2,0,sizeof(t2));
for(int i = 0;i < A.len;++i) t1[i] = A.a[i];
for(int i = 0;i < B.len;++i) t2[i] = B.a[i];
// printf("%d\n",tot);
NTT(t1,tot,1);NTT(t2,tot,1);
for(int i = 0;i <= tot;++i) t1[i] = 1ll * t1[i] * t2[i] % mod;
NTT(t1,tot,-1);
// for(int i = 0;i <= tot;++i) printf("%d ",t1[i]); puts("");
int tmp = qm(tot,mod - 2);
BIGNUM C;
C.len = len;
for(int i = 0;i <= tot;++i)
t1[i] = 1ll * t1[i] * tmp % mod;
for(int i = 0;i < C.len;++i) {
if(t1[i] >= 10) {
t1[i + 1] += t1[i] / 10;
t1[i] %= 10;
if(i == C.len - 1) ++C.len;
}
C.a[i] = t1[i];
// printf("%d ",t1[i]);
}
// printf("%d%d\n",C.a[0],C.a[1]);
// puts("");
return C;
}
int main() {
n.Read();
// BIGNUM K = (n + 1) * (n + 2);
BIGNUM K = (n + 1) * (n + 2) * (n + 3) * (n + 4) / 24;
for(int i = K.len - 1;i >= 0;--i) printf("%d",K.a[i]);
return 0;
}