本文完成的时间跨度较长,文风变化可能较大……

最近更新于2020年2月17日

Part 1.主线

乘法

前面讲过FFT

然而FFT常数感人,适用范围还窄,比如不能取模

于是有了NTT

其实就是取模的FFT

FFT 需要用到复数ω=cos(2πn)+sin(2πn)i\omega=cos(\frac{2\pi}{n})+sin(\frac{2\pi}{n})iω=cos(n2π​)+sin(n2π​)i,因为具有循环卷积性质

即ωn=1,ωk(1≤k≤n)\omega^n=1,\omega^k(1\leq k\leq n)ωn=1,ωk(1≤k≤n)互不相同

我们希望找一个模意义下的替代品

显然就是原根

具体地说,w≡gMOD−1n(modMOD)w\equiv g^{\frac{MOD-1}{n}}\pmod {MOD}w≡gnMOD−1​(modMOD)

所以模数MODMODMOD需要满足

  1. 有原根
  2. MOD=p×2k+1MOD=p\times 2^k+1MOD=p×2k+1,其中2k>n2^k>n2k>n

最常用的是998244353998244353998244353

有时会用3个,就记469762049,998244353,1004535809469762049,998244353,1004535809469762049,998244353,1004535809

这三个原根都是333

什么?记不住?

那就记ppp:7,119,4797,119,4797,119,479,考场上拿计算器算一下就可以了

然后和 FFT 没啥差别

下面贴的是自己摸索出的最方便的版本,会持续更新。和后面的代码会有不同。

typedef long long ll;
inline int add(const int& x,const int& y){return x+y>=MOD? x+y-MOD:x+y;}
inline int dec(const int& x,const int& y){return x<y? x-y+MOD:x-y;}
inline int qpow(int a,int p)
{int ans=1;while (p){if (p&1) ans=(ll)ans*a%MOD;a=(ll)a*a%MOD;p>>=1;}return ans;
}
#define inv(x) qpow(x,MOD-2)
int rt[2][24];
int l,lim,r[MAXN];
inline void init(){lim=1<<l;for (int i=0;i<lim;i++) r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));}
inline void NTT(int* a,int type)
{for (int i=0;i<lim;i++) if (i<r[i]) swap(a[i],a[r[i]]);for (int L=0;L<l;L++){int mid=1<<L,len=mid<<1,Wn=rt[type][L+1];for (int s=0;s<lim;s+=len){ll w=1;for (int k=0;k<mid;k++,w=w*Wn%MOD){int x=a[s+k],y=w*a[s+mid+k]%MOD;a[s+k]=add(x,y);a[s+mid+k]=dec(x,y);}            }}if (type){int t=inv(lim);for (int i=0;i<lim;i++) a[i]=(ll)a[i]*t%MOD;}
}
int main()
{rt[0][23]=qpow(3,119);rt[1][23]=inv(rt[0][23]);for (int i=22;i>=0;i--){rt[0][i]=(ll)rt[0][i+1]*rt[0][i+1]%MOD;rt[1][i]=(ll)rt[1][i+1]*rt[1][i+1]%MOD;}/*do something*/return 0;
}

求逆

给定项数为nnn的多项式AAA,求BBB使得A×B≡1(modxn)A \times B \equiv1(mod \text{ }x^n)A×B≡1(mod xn),系数对998244353取模998244353取模998244353取模

A×B≡1(modxn)A \times B \equiv1\pmod {x^n}A×B≡1(modxn)


A×B′≡1(modx⌈n2⌉)A \times B' \equiv1\pmod {x^{\lceil \frac{n}{2}\rceil}}A×B′≡1(modx⌈2n​⌉)

两式相减

A×(B−B′)≡0(modx⌈n2⌉)A \times (B-B') \equiv0\pmod {x^{\lceil \frac{n}{2}\rceil}}A×(B−B′)≡0(modx⌈2n​⌉)

假装AAA不为0

B−B′≡0(modx⌈n2⌉)B-B' \equiv0\pmod {x^{\lceil \frac{n}{2}\rceil}}B−B′≡0(modx⌈2n​⌉)

我们要求的是xnx^nxn,所以平方一下

B2−2BB′+B′2≡0(modxn)B^2-2BB'+B'^2 \equiv0\pmod {x^n}B2−2BB′+B′2≡0(modxn)

看BBB不爽,乘个AAA消掉

B−2B′+AB′2≡0(modxn)B-2B'+AB'^2 \equiv0\pmod {x^n}B−2B′+AB′2≡0(modxn)

B≡2B′−AB′2(modxn)B\equiv2B'-AB'^2 \pmod {x^n}B≡2B′−AB′2(modxn)

然后就可以递归了

边界时n=1n=1n=1,直接求逆元

注意取模

复杂度O(nlog⁡n)O(n\log n)O(nlogn)常数跟LCT有的一拼

int a[MAXN],b[MAXN];
void getinv(int *A,int n)
{if (n==1) return (void)(b[0]=inv(A[0]));getinv(A,(n+1)>>1);int l=0;while ((1<<l)<(n<<1)) ++l;init(l);for (int i=0;i<n;i++) a[i]=A[i];for (int i=n;i<(1<<l);i++) a[i]=0;NTT(a,l,1);NTT(b,l,1);for (int i=0;i<(1<<l);i++) b[i]=(ll)b[i]*(MOD+2-(ll)a[i]*b[i]%MOD)%MOD;NTT(b,l,-1);for (int i=n;i<(1<<l);i++) b[i]=0;
}

对数函数

给定F(x)F(x)F(x),求G(x)≡ln⁡(F(x))(modxn)G(x) \equiv \ln(F(x))(mod\text{ }x^n)G(x)≡ln(F(x))(mod xn)。系数对998244353998244353998244353取模

前置知识

求导和积分

自行百度

对数函数求导

f(x)=ln⁡(x)f(x)=\ln(x)f(x)=ln(x)

f′(x)=1xf'(x)=\frac{1}{x}f′(x)=x1​

多项式求导和积分

其实就是幂函数

f(x)=xnf(x)=x^nf(x)=xn

f′(x)=nxn−1f'(x)=nx^{n-1}f′(x)=nxn−1

积分是逆运算

∫f(x)dx=1n+1xn+1\int f(x)dx=\frac{1}{n+1}x^{n+1}∫f(x)dx=n+11​xn+1

所有项加起来即可

复合函数

h(x)=f(g(x))h(x)=f(g(x))h(x)=f(g(x))

h′(x)=f′(g(x))g′(x)h'(x)=f'(g(x))g'(x)h′(x)=f′(g(x))g′(x)

注意:f(g(x))f(g(x))f(g(x))的自变量是g(x)g(x)g(x)

正文

推式子


f(x)=ln⁡(x)f(x)=\ln(x)f(x)=ln(x)

G(x)≡f(F(x))(modxn)G(x) \equiv f(F(x)) \pmod {x^n}G(x)≡f(F(x))(modxn)

同时求导

G′(x)≡f′(F(x))F′(x)(modxn)G'(x) \equiv f'(F(x))F'(x) \pmod {x^n}G′(x)≡f′(F(x))F′(x)(modxn)

G′(x)≡F′(x)F(x)(modxn)G'(x) \equiv \frac{F'(x)}{F(x)} \pmod {x^n}G′(x)≡F(x)F′(x)​(modxn)

求导求逆乘起来,然后求积分即可

注意取模

inline void deriv(int *a,int *b,int n)
{for (int i=0;i<n-1;i++) b[i]=(ll)a[i+1]*(i+1)%MOD;b[n-1]=0;
}
inline void integ(int *a,int *b,int n)
{for (int i=1;i<n;i++) b[i]=(ll)a[i-1]*inv(i)%MOD;b[0]=0;
}
int F[MAXN],f[MAXN],G[MAXN];
int main()
{int n;scanf("%d",&n);for (int i=0;i<n;i++) scanf("%d",&F[i]);deriv(F,f,n);getinv(F,n);int l=0;while ((1<<l)<(n<<1)) ++l;init(l);NTT(f,l,1);NTT(b,l,1);for (int i=0;i<(1<<l);i++) f[i]=(ll)f[i]*b[i]%MOD;NTT(f,l,-1);integ(f,G,n);for (int i=0;i<n;i++) printf("%d ",G[i]);return 0;
}

然而之前一直有个问题,多项式怎么取对数?取了对数至少是无理数吧,为什么还能取模?

抱着疑惑,我们不取模,手算一下:

F(x)=x+1F(x)=x+1F(x)=x+1

F′(x)=1F'(x)=1F′(x)=1

G′(x)=F′(x)F(x)=1x+1G'(x)=\frac{F'(x)}{F(x)}=\frac{1}{x+1}G′(x)=F(x)F′(x)​=x+11​

G(x)=ln⁡(x+1)G(x)=\ln(x+1)G(x)=ln(x+1)

根本不是多项式

也就是说,取对数只是加了个ln⁡\lnln

只是为了便于后期处理,通过取模把这玩意映射成了多项式。

(准确地说,是把它泰勒展开成多项式然后丢掉了高位。关于泰勒展开的姿势马上就会讲到)

和有理数取模一个道理。

多项式指数函数

给定F(x)F(x)F(x)(常数项为000),求G(x)≡eF(x)(modxn)G(x) \equiv e^{F(x)} \pmod {x^n}G(x)≡eF(x)(modxn)。系数对998244353998244353998244353取模

前置知识

泰勒展开

对于一个不方便直接求值的函数fff,求某个位置的值f(x)f(x)f(x)

用多项式来逼近,具体步骤是强制让每一阶导数都相等。记这个多项式为GGG

取一点x0x_0x0​

f(x)=G(x)=∑i=0∞f(i)(x0)i!(x−x0)if(x)=G(x)=\sum_{i=0}^{\infty}\frac{f^{(i)}(x_0)}{i!}(x-x_0)^if(x)=G(x)=i=0∑∞​i!f(i)(x0​)​(x−x0​)i

(f(i)f^{(i)}f(i)表示fff的iii阶导数)

网上资料很多,不再细讲

正文

G(x)≡eF(x)(modxn)G(x) \equiv e^{F(x)} \pmod {x^n}G(x)≡eF(x)(modxn)

取对数

ln⁡(G(x))≡F(x)(modxn)\ln(G(x)) \equiv F(x) \pmod {x^n}ln(G(x))≡F(x)(modxn)

ln⁡(G(x))−F(x)≡0(modxn)\ln(G(x)) -F(x) \equiv 0\pmod {x^n}ln(G(x))−F(x)≡0(modxn)

现在要求G(x)G(x)G(x)

f(G(x))≡ln⁡(G(x))−F(x)(modxn)f(G(x)) \equiv \ln(G(x)) -F(x)\pmod {x^n}f(G(x))≡ln(G(x))−F(x)(modxn)

现在要求这玩意的零点(没错,零点是个多项式)

f(G(x))≡0(modxn)f(G(x)) \equiv 0\pmod {x^n}f(G(x))≡0(modxn)

假设求出了f(G0(x))≡0(modx⌈n2⌉)f(G_0(x))\equiv 0\pmod {x^{\lceil \frac{n}{2}\rceil}}f(G0​(x))≡0(modx⌈2n​⌉)

把f(G(x))f(G(x))f(G(x))在G0(x)G_0(x)G0​(x)处泰勒展开

f(G(x))=f(G0(x))+f′(G0(x))1!(G(x)−G0(x))+f′′(G0(x))2!(G(x)−G0(x))2+……f(G(x))=f(G_0(x))+\frac{f'(G_0(x))}{1!}(G(x)-G_0(x))+\frac{f''(G_0(x))}{2!}(G(x)-G_0(x))^2+\dots\dotsf(G(x))=f(G0​(x))+1!f′(G0​(x))​(G(x)−G0​(x))+2!f′′(G0​(x))​(G(x)−G0​(x))2+……

因为G(x)≡G0(x)(modx⌈n2⌉)G(x) \equiv G_0(x)\pmod {x^{\lceil \frac{n}{2}\rceil}}G(x)≡G0​(x)(modx⌈2n​⌉)……

什么?为什么?

首先f(G(x))≡0(modx⌈n2⌉)f(G(x))\equiv 0\pmod {x^{\lceil \frac{n}{2}\rceil}}f(G(x))≡0(modx⌈2n​⌉)

因为方程有唯一解(不然没有意义),所以G(x)G(x)G(x)和G0(x)G_0(x)G0​(x)是一个东西 但是在模意义下,所以G(x)G(x)G(x)需要取模

好继续

因为

G(x)≡G0(x)(modx⌈n2⌉)G(x) \equiv G_0(x)\pmod {x^{\lceil \frac{n}{2}\rceil}}G(x)≡G0​(x)(modx⌈2n​⌉)

所以(G(x)−G0(x))2≡0(modxn)(G(x)-G_0(x))^2\equiv0\pmod {x^n}(G(x)−G0​(x))2≡0(modxn)

发现上面那个展开式后面全没了

所以

f(G(x))≡f(G0(x))+f′(G0(x))(G(x)−G0(x))(modxn)f(G(x))\equiv f(G_0(x))+f'(G_0(x))(G(x)-G_0(x))\pmod {x^n}f(G(x))≡f(G0​(x))+f′(G0​(x))(G(x)−G0​(x))(modxn)

左边不是000吗

G(x)≡G0(x)−f(G0(x))f′(G0(x))(modxn)G(x)\equiv G_0(x)-\frac{f(G_0(x))}{f'(G_0(x))}\pmod {x^n}G(x)≡G0​(x)−f′(G0​(x))f(G0​(x))​(modxn)

这就是传说中的多项式牛顿迭代,但好像没啥关系。

观察一下,我们需要求f′f'f′

f(G(x))=ln⁡(G(x))−F(x)f(G(x)) = \ln(G(x)) -F(x)f(G(x))=ln(G(x))−F(x)

由于自变量是G(x)G(x)G(x),F(x)F(x)F(x)就是常数

f′(G(x))=1G(x)f'(G(x))=\frac{1}{G(x)}f′(G(x))=G(x)1​

代回去

G(x)≡G0(x)−(ln(G0(x))−F(x))G0(x)(modxn)G(x) \equiv G_0(x)-(ln(G_0(x))-F(x))G_0(x)\pmod {x^n}G(x)≡G0​(x)−(ln(G0​(x))−F(x))G0​(x)(modxn)

G(x)≡G0(x)(1−ln(G0(x))+F(x))(modxn)G(x) \equiv G_0(x)(1-ln(G_0(x))+F(x))\pmod {x^n}G(x)≡G0​(x)(1−ln(G0​(x))+F(x))(modxn)

然后就可以递归啦

边界:n=1n=1n=1时,ln⁡(G(x))\ln(G(x))ln(G(x))和F(x)F(x)F(x)常数项相等

题目保证为000,所以G(x)=1G(x)=1G(x)=1

复杂度O(nlog⁡n)O(n\log n)O(nlogn) 常数感人

int f[MAXN];
void getexp(int* A,int* B,int n)
{if (n==1) return (void)(*B=1);getexp(A,B,(n+1)>>1);int l=0;while ((1<<l)<(n<<1)) ++l;init(l);getln(B,f,n);for (int i=0;i<n;i++) {f[i]=A[i]-f[i];if (f[i]<0) f[i]+=MOD;}++f[0];for (int i=n;i<(1<<l);i++) B[i]=f[i]=0;NTT(B,l,1);NTT(f,l,1);for (int i=0;i<(1<<l);i++) B[i]=(ll)f[i]*B[i]%MOD;NTT(B,l,-1);
}

幂函数

给定F(x)F(x)F(x)(常数项为111),求G(x)≡Fk(x)(modxn)G(x) \equiv F^k(x)\pmod {x^n}G(x)≡Fk(x)(modxn)。系数模998244353998244353998244353。

G(x)≡Fk(x)(modxn)G(x) \equiv F^k(x)\pmod {x^n}G(x)≡Fk(x)(modxn)

同时取对数

ln⁡(G(x))≡kln⁡(F(x))(modxn)\ln(G(x)) \equiv k\ln(F(x))\pmod {x^n}ln(G(x))≡kln(F(x))(modxn)

求出ln⁡\lnln,乘以kkk,再exp⁡\expexp回去

因为函数衔接的地方有很多细节,这里贴一个用到幂函数的代码,可供参考

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cctype>
#define MAXN 400005
using namespace std;
inline int read()
{int ans=0;char c=getchar();while (!isdigit(c)) c=getchar();while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();return ans;
}
const int MOD=950009857;
typedef long long ll;
inline int add(const int& x,const int& y){return x+y>=MOD? x+y-MOD:x+y;}
inline int dec(const int& x,const int& y){return x<y? x-y+MOD:x-y;}
inline int qpow(int a,int p)
{int ans=1;while (p){if (p&1) ans=(ll)ans*a%MOD;a=(ll)a*a%MOD;p>>=1;}return ans;
}
#define inv(x) qpow(x,MOD-2)
int rt[2][22];
int l,r[MAXN];
inline void init(){for (int i=0;i<(1<<l);i++) r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));}
inline void NTT(int* a,int type)
{int lim=1<<l;for (int i=0;i<lim;i++) if (i<r[i]) swap(a[i],a[r[i]]);for (int L=0;L<l;L++){int mid=1<<L,len=mid<<1,Wn=rt[type][L+1];for (int s=0;s<lim;s+=len)for (int k=0,w=1;k<mid;k++,w=(ll)w*Wn%MOD){int x=a[s+k],y=(ll)a[s+mid+k]*w%MOD;a[s+k]=add(x,y),a[s+mid+k]=dec(x,y);}}if (type){int t=inv(lim);for (int i=0;i<lim;i++) a[i]=(ll)a[i]*t%MOD;}
}
void getinv(int* A,int* B,int n)
{static int f[MAXN],t[MAXN];if (n==1) return (void)(*B=inv(*A));getinv(A,t,(n+1)>>1);l=0;while ((1<<l)<(n<<1)) ++l;init();for (int i=0;i<n;i++) f[i]=A[i];for (int i=n;i<(1<<l);i++) f[i]=t[i]=0;NTT(f,0);NTT(t,0);for (int i=0;i<(1<<l);i++) B[i]=(ll)t[i]*dec(2,(ll)f[i]*t[i]%MOD)%MOD;NTT(B,1);for (int i=n;i<(1<<l);i++) B[i]=0;
}
inline void deriv(int* A,int* B,int n){for (int i=0;i<n-1;i++) B[i]=(ll)A[i+1]*(i+1)%MOD;B[n-1]=0;}
inline void integ(int* A,int* B,int n){for (int i=1;i<n;i++) B[i]=(ll)A[i-1]*inv(i)%MOD;B[0]=0;}
void getln(int* A,int* B,int n)
{static int f[MAXN],g[MAXN];deriv(A,f,n);getinv(A,g,n);for (int i=n;i<(1<<l);i++) f[i]=g[i]=0;NTT(f,0);NTT(g,0);for (int i=0;i<(1<<l);i++) f[i]=(ll)f[i]*g[i]%MOD;NTT(f,1);integ(f,B,n);for (int i=n;i<(1<<l);i++) B[i]=0;
}
void getexp(int* A,int* B,int n)
{static int f[MAXN],g[MAXN];if (n==1) return (void)(*B=1);getexp(A,g,(n+1)>>1);getln(g,f,n);for (int i=0;i<n;i++) f[i]=dec(A[i],f[i]);++f[0];for (int i=n;i<(1<<l);i++) f[i]=g[i]=0;NTT(f,0);NTT(g,0);for (int i=0;i<(1<<l);i++) B[i]=(ll)f[i]*g[i]%MOD;NTT(B,1);for (int i=n;i<(1<<l);i++) B[i]=0;
}
int a[MAXN],t[MAXN];
int main()
{rt[0][21]=qpow(5,453);rt[1][21]=inv(rt[0][21]);for (int i=20;i>=0;i--){rt[0][i]=(ll)rt[0][i+1]*rt[0][i+1]%MOD;rt[1][i]=(ll)rt[1][i+1]*rt[1][i+1]%MOD;}/*略*/return 0;
}

分治NTT

首先,洛谷上分治NTT板子其实没啥用……

更常用的是类似于

∏i=1n(x+ai)\prod_{i=1}^n(x+a_i)i=1∏n​(x+ai​)

解决的方法是分成两半递归计算,然后一次NTT乘起来 复杂度是O(nlog⁡2n)O(n\log^2n)O(nlog2n)

注意这个算法的核心并不是减少NTT次数(NTT次数仍然是n−1n-1n−1)而是在NTT的时候控制两边多项式的长度,使它是成倍增加的,这样短的多项式所耗的时间在长多项式面前不值一提。

所以在回溯到上一层之后会正向执行NTT,但你在这一层还是要反向NTT回去,因为两层NTT的长度不同。如果贸然使用上一层的长度会让复杂度退化。

还有临时数组必须每次递归单独开,并且长度要和区间长度相关。(但一般都不是区间长度)

给份伪代码

void solve(int l,int r,int F[])
{if (l==r) get(F,l),return;//将当前一位的值给F,返回int L[(r-l+1)<<1],R[(r-l+1)<<1];solve(l,mid,L),solve(mid+1,r,R);NTT(L),NTT(R);F=L*R;INTT(F);
}

几个经验

  • 各个函数会来回调用,不建议共用临时数组。可以在函数里面开static避免重名。
  • 在 NTT 前后取模可以万无一失
  • 控制一下语句顺序可以只在求逆的时候预处理 NTT

Part2.毒瘤线

除法

给定F,GF,GF,G,求Q,RQ,RQ,R使得F=QG+RF=QG+RF=QG+R,其中deg(R)<deg(G)deg(R)<deg(G)deg(R)<deg(G)。系数对998244353998244353998244353取模。

如果能去掉余数,就可以一波逆元算过去,可惜去不得。

所以考虑把RRR模掉

记Fr(x)F_r(x)Fr​(x)表示F(x)F(x)F(x)系数翻转

显然Fr(x)=xnF(1x)F_r(x)=x^nF(\frac{1}{x})Fr​(x)=xnF(x1​)(nnn为最高项次数)

推式子

F(x)=Q(x)G(x)+R(x)F(x)=Q(x)G(x)+R(x)F(x)=Q(x)G(x)+R(x)

F(1x)=Q(1x)G(1x)+R(1x)F(\frac{1}{x})=Q(\frac{1}{x})G(\frac{1}{x})+R(\frac{1}{x})F(x1​)=Q(x1​)G(x1​)+R(x1​)

两边同时乘以xnx^nxn

xnF(1x)=xn−mQ(1x)xmG(1x)+xn−m+1xm−1R(1x)x^nF(\frac{1}{x})=x^{n-m}Q(\frac{1}{x})x^mG(\frac{1}{x})+x^{n-m+1}x^{m-1}R(\frac{1}{x})xnF(x1​)=xn−mQ(x1​)xmG(x1​)+xn−m+1xm−1R(x1​)

Fr(x)=Qr(x)Gr(x)+xn−m+1Rr(x)F_r(x)=Q_r(x)G_r(x)+x^{n-m+1}R_r(x)Fr​(x)=Qr​(x)Gr​(x)+xn−m+1Rr​(x)

蛤蛤蛤

Fr(x)≡Qr(x)Gr(x)(modxn−m+1)F_r(x) \equiv Q_r(x)G_r(x)\pmod {x^{n-m+1}}Fr​(x)≡Qr​(x)Gr​(x)(modxn−m+1)

求一波逆算出QQQ,再在原式中算出RRR

int main()
{int n,m;scanf("%d%d",&n,&m);for (int i=0;i<=n;i++) scanf("%d",&F[i]);for (int i=0;i<=m;i++) scanf("%d",&G[i]);reverse(F,F+n+1);reverse(G,G+m+1);getinv(G,n-m+1);int l=0;while ((1<<l)<(n<<1)) ++l;init(l);memcpy(t,F,sizeof(t));for (int i=n-m+2;i<=n;i++) F[i]=0;NTT(F,l,1);NTT(b,l,1);for (int i=0;i<(1<<l);i++) Q[i]=(ll)F[i]*b[i]%MOD;NTT(Q,l,-1);memcpy(F,t,sizeof(F));for (int i=n-m+1;i<=n+m;i++) Q[i]=0;reverse(F,F+n+1);reverse(G,G+m+1);reverse(Q,Q+n-m+1);NTT(F,l,1);NTT(G,l,1);NTT(Q,l,1);for (int i=0;i<(1<<l);i++) R[i]=(MOD+F[i]-(ll)Q[i]*G[i]%MOD)%MOD;NTT(R,l,-1);NTT(Q,l,-1);for (int i=0;i<=n-m;i++) printf("%d ",Q[i]);puts("");for (int i=0;i<=m-1;i++) printf("%d ",R[i]);return 0;
}

多项式取模

除法算出来再乘回去减一下就可以了

多点求值

给一个多项式F(x)F(x)F(x),给出a1,a2,...,ama_1,a_2,...,a_ma1​,a2​,...,am​,求所有F(ai)F(a_i)F(ai​)

n,m≤64000n,m\leq64000n,m≤64000

显然肯定不能一个一个求,因为你系数都遍历不完

所以多半是分治

对于一个区间[L,R][L,R][L,R],求出多项式

G(x)=∏LR(x−ai)G(x)=\prod_{L}^{R}(x-a_i)G(x)=L∏R​(x−ai​)
将F(x)F(x)F(x)除以G(x)G(x)G(x),即F(x)=D(x)G(x)+R(x)F(x)=D(x)G(x)+R(x)F(x)=D(x)G(x)+R(x)

代入aia_iai​,我们发现G(ai)=0G(a_i)=0G(ai​)=0,所以F(ai)=R(ai)F(a_i)=R(a_i)F(ai​)=R(ai​)

RRR的规模每次减半,所以可以递归了。

复杂度O(nlog⁡2n)O(n\log^2 n)O(nlog2n)

具体实现的时候要先分治一次,用类似线段树的结构把所有用到的区间的GGG存起来

这里开了nnn个vector,后面的快速插值中用了一个指针数组动态申请内存……

常数极大,建议递归到较小长度的时候暴力秦九韶

闲着没事别在考场上写……

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cctype>
#include <vector>
#include <algorithm>
#define MAXN 262144
#define re register
#define MOD 998244353
using namespace std;
typedef long long ll;
inline int qpow(int a,int p)
{int ans=1;while (p){if (p&1) ans=(ll)ans*a%MOD;a=(ll)a*a%MOD;p>>=1;}return ans;
}
namespace In
{# define In_Len 2000000static std :: streambuf* fb ( std :: cin.rdbuf ( ) ) ;static char buf [In_Len], *ss ( 0 ) ;void In_init ( )  {  fb -> sgetn ( ss = buf, In_Len ) ;  }inline int read ( )  {register int x ;bool opt ( 1 ) ;while ( isspace ( *ss ) )  ++ ss ;if ( *ss == 45 )  { ++ ss ; opt = 0 ; }for ( x = -48 + *ss ; isdigit ( * ++ ss ) ; ( x *= 10 ) += *ss - 48 ) ; ++ ss ;return opt ? x : -x ;}
# undef In_Len
}
using namespace In;
namespace Out
{# define Out_Len 2000000static std :: streambuf* fb ( std :: cout.rdbuf ( ) ) ;static char buf [Out_Len], *ss ( buf ) ;inline void write ( register int x )  {static int T [30], tp ( 0 ) ;if ( ! x )  {  *ss ++ =  48 ; *ss ++ = 10 ; return ;  }if ( x < 0 )  {  *ss ++ = 45 ; x = -x ;  }while ( x ) T [++ tp] = x % 10 | 48, x /= 10 ;while ( tp )  *ss ++ = T [tp --] ;*ss ++ = 10 ;}inline void flush ( )  {  fb -> sputn ( buf, ss - buf ) ;  }
# undef Out_Len
}
using namespace Out;
inline int add(const int& x,const int& y){return x+y>=MOD? x+y-MOD:x+y;}
inline int dec(const int& x,const int& y){return x<y? x-y+MOD:x-y;}
#define inv(x) qpow(x,MOD-2)
int r[MAXN],rt[2][24];
inline void init(int l){for (re int i=0;i<(1<<l);++i) r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));}
void NTT(int* a,int l,int type)
{type=(1-type)>>1;int lim=1<<l;for (re int i=0;i<lim;++i) if (i<r[i]) a[i]^=a[r[i]]^=a[i]^=a[r[i]];for (re int L=0;L<l;++L){int mid=1<<L,len=mid<<1;int Wn=rt[type][L+1];
//      int Wn=qpow(3,(MOD-1)/(mid<<1));
//      if (type==-1) Wn=inv(Wn);for (re int s=0;s<lim;s+=len)for (re ll w=1,k=0;k<mid;++k,w=w*Wn%MOD){const ll x=a[s+k],y=w*a[s+mid+k]%MOD;a[s+k]=add(x,y);a[s+mid+k]=dec(x,y);}}if (type){int t=inv(lim);for (int i=0;i<lim;++i) a[i]=(ll)a[i]*t%MOD;}
}
void getinv(int* A,int* B,int n)
{static int t[MAXN];if (n==1) return (void)(*B=inv(*A));getinv(A,B,(n+1)>>1);int l=0;while ((1<<l)<(n<<1)) ++l;init(l);for (re int i=0;i<n;++i) t[i]=A[i];for (re int i=n;i<(1<<l);++i) t[i]=B[i]=0;NTT(t,l,1);NTT(B,l,1);for (re int i=0;i<(1<<l);++i) B[i]=(ll)B[i]*(MOD+2-(ll)B[i]*t[i]%MOD)%MOD;NTT(B,l,-1);for (re int i=n;i<(1<<l);++i) B[i]=0;
}
void getmod(int* A,int* B,int *R,int n,int m)
{static int f[MAXN],g[MAXN],d[MAXN],t[MAXN];for (re int i=0;i<=n;++i) f[i]=A[n-i];for (re int i=0;i<=m;++i) g[i]=B[m-i];getinv(g,t,n-m+1);int l=0;while ((1<<l)<=n*2) ++l;init(l);for (re int i=n-m+1;i<(1<<l);++i) f[i]=t[i]=0;NTT(f,l,1);NTT(t,l,1);for (re int i=0;i<(1<<l);++i) d[i]=(ll)f[i]*t[i]%MOD;NTT(d,l,-1);for (re int i=n-m+1;i<(1<<l);++i) d[i]=0;for (re int i=0;i<=n;++i) f[i]=A[i];for (re int i=0;i<=m;++i) g[i]=B[i];for (re int i=n+1;i<(1<<l);++i) f[i]=0;for (re int i=m+1;i<(1<<l);++i) g[i]=0;for (re int l=0,r=n-m;l<r;d[l]^=d[r]^=d[l]^=d[r],++l,--r);NTT(d,l,1);NTT(g,l,1);for (re int i=0;i<(1<<l);++i) R[i]=(ll)d[i]*g[i]%MOD;NTT(R,l,-1);for (re int i=0;i<m;++i) R[i]=(f[i]+MOD-R[i])%MOD;for (re int i=m;i<(1<<l);++i) R[i]=0;
}
int x[MAXN];
#define lc p<<1
#define rc p<<1|1
vector<int> v[MAXN<<2];
void cdqNTT(int p,int l,int r)
{v[p].resize(r-l+2);if (l==r) return (void)(v[p][0]=MOD-x[l],v[p][1]=1);const int mid=(l+r)>>1;cdqNTT(lc,l,mid);cdqNTT(rc,mid+1,r);int len=0;while ((1<<len)<=(r-l+1)) ++len;init(len);int L[1<<len],R[1<<len];for (re int i=0;i<=mid-l+1;++i) L[i]=v[lc][i];for (re int i=0;i<=r-mid;++i) R[i]=v[rc][i];  for (re int i=mid-l+2;i<(1<<len);++i) L[i]=0;for (re int i=r-mid+1;i<(1<<len);++i) R[i]=0;NTT(L,len,1);NTT(R,len,1);for (re int i=0;i<(1<<len);++i) L[i]=(ll)L[i]*R[i]%MOD;NTT(L,len,-1);for (re int i=0;i<=(r-l+1);++i) v[p][i]=L[i];
//  delete(L);delete(R);
}
int n,m;
int ans[MAXN];
void solve(int p,int l,int r,int* f)
{if (r-l<=850){for (re int i=l;i<=r;++i)for (re int j=0,w=1;j<=r-l;++j,w=(ll)w*x[i]%MOD)ans[i]=(ans[i]+(ll)f[j]*w)%MOD;return;}
//  if (l==r) return (void)(ans[l]=*f);const int mid=(l+r)>>1;int cur[(r-l+1)<<2],L[(r-l+1)<<2];memset(cur,0,sizeof(cur));for (re int i=0;i<=mid-l+1;++i) cur[i]=v[lc][i];getmod(f,cur,L,r-l,mid-l+1);solve(lc,l,mid,L);for (re int i=0;i<=r-mid;++i) cur[i]=v[rc][i];for (re int i=r-mid+1;i<=mid-l+1;++i) cur[i]=0;getmod(f,cur,L,r-l,r-mid);    solve(rc,mid+1,r,L);
}
int f[MAXN],t[MAXN],a[MAXN];
int main()
{In_init(); rt[0][23]=qpow(3,119);rt[1][23]=inv(rt[0][23]);for (int i=22;i>=0;--i) rt[0][i]=(ll)rt[0][i+1]*rt[0][i+1]%MOD,rt[1][i]=(ll)rt[1][i+1]*rt[1][i+1]%MOD;n=read();m=read();for (re int i=0;i<=n;++i) f[i]=read();for (re int i=1;i<=m;++i) x[i]=read();cdqNTT(1,1,m);if (n>=m){for (re int i=0;i<=m;++i) t[i]=v[1][i];memcpy(a,f,sizeof(a));getmod(a,t,f,n,m);}solve(1,1,m,f);for (re int i=1;i<=m;++i) write(ans[i]);flush();return 0;
}

快速插值

给定nnn个点(xi,yi)(x_i,y_i)(xi​,yi​),求一个过这nnn个点的n−1n-1n−1次多项式

xxx互不相同

考虑拉格朗日插值

f(x)=∑i=1nyi∏i≠jx−xjxi−xjf(x)=\sum_{i=1}^ny_i\prod_{i\neq j}\frac{x-x_j}{x_i-x_j}f(x)=i=1∑n​yi​i​=j∏​xi​−xj​x−xj​​

分式看着很烦,把它拆开

f(x)=∑i=1nyi∏i≠j(xi−xj)∏i≠j(x−xj)f(x)=\sum_{i=1}^n{{y_i}\over{\prod_{i\neq j}(x_i-x_j)}}\prod_{i\neq j}(x-x_j)f(x)=i=1∑n​∏i​=j​(xi​−xj​)yi​​i​=j∏​(x−xj​)

考虑左边这坨怎么求

yi∏i≠j(xi−xj){{y_i}\over{\prod_{i\neq j}(x_i-x_j)}}∏i​=j​(xi​−xj​)yi​​

上面是个常数,所以要求下面

我们设

g(x)=∏i=1n(x−xi)g(x)=\prod_{i=1}^n(x-x_i)g(x)=i=1∏n​(x−xi​)

那么

∏i≠j(x−xj)\prod_{i\neq j}(x-x_j)i​=j∏​(x−xj​)

可以写成

g(xi)x−xi\frac{g(x_i)}{x-x_i}x−xi​g(xi​)​

代入x=xix=x_ix=xi​得到……

哎怎么分母是000啊

仔细算了一下,发现上面也是000

这时候就要考虑洛必达法则了

因为x→0x\to0x→0的时候上下同时趋近于000,它们的比值等于它们导数的比值

所以等于

g′(xi)g'(x_i)g′(xi​)

代回原式

f(x)=∑i=1nyig′(xi)∏i≠j(x−xj)f(x)=\sum_{i=1}^n{{y_i}\over{g'(x_i)}}\prod_{i\neq j}(x-x_j)f(x)=i=1∑n​g′(xi​)yi​​i​=j∏​(x−xj​)

我们先分治算出g(x)g(x)g(x),求个导算出g′(x)g'(x)g′(x),然后多 点 求 值算出所有g′(xi)g'(x_i)g′(xi​),相当于是常数,我们记

yi′=yig′(xi)y'_i={{y_i}\over{g'(x_i)}}yi′​=g′(xi​)yi​​

f(x)=∑i=1nyi′∏i≠j(x−xj)f(x)=\sum_{i=1}^ny'_i\prod_{i\neq j}(x-x_j)f(x)=i=1∑n​yi′​i​=j∏​(x−xj​)

当然如果xxx是连续正整数或者有其他一些奇妙的性质,你也许可以直接用阶乘之类的算出yi′y'_iyi′​

现在考虑这个怎么求

考虑分治

fl,r(x)=∑i=lryi′∏i=l,i≠jr(x−xj)f_{l,r}(x)=\sum_{i=l}^ry'_i\prod_{i=l,i\neq j}^r(x-x_j)fl,r​(x)=i=l∑r​yi′​i=l,i​=j∏r​(x−xj​)

mid=(l+r)>>1

考虑fl,mid(x)f_{l,mid}(x)fl,mid​(x),它并没有计算∏i≠j(x−xj)\prod_{i\neq j}(x-x_j)∏i​=j​(x−xj​)的[mid+1,r][mid+1,r][mid+1,r]的部分,但是因为iii在左边,右边没有缺口,所以直接乘上∏i=mid+1r(x−xi)\prod_{i=mid+1}^r(x-x_i)∏i=mid+1r​(x−xi​)即可

同理可得

fl,r(x)=fl,mid(x)gmid+1,r(x)+gl,mid(x)fmid+1,r(x)f_{l,r}(x)=f_{l,mid}(x)g_{mid+1,r}(x)+g_{l,mid}(x)f_{mid+1,r}(x)fl,r​(x)=fl,mid​(x)gmid+1,r​(x)+gl,mid​(x)fmid+1,r​(x)

递归即可,复杂度O(nlog⁡2n)O(n\log^2n)O(nlog2n)

瓶颈是多点求值,各种意义上

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cctype>
#include <ctime>
#define MAXN 524288
using namespace std;
namespace In
{# define In_Len 2000000static std :: streambuf* fb ( std :: cin.rdbuf ( ) ) ;static char buf [In_Len], *ss ( 0 ) ;void In_init ( )  {  fb -> sgetn ( ss = buf, In_Len ) ;  }inline int read ( )  {register int x ;bool opt ( 1 ) ;while ( isspace ( *ss ) )  ++ ss ;if ( *ss == 45 )  { ++ ss ; opt = 0 ; }for ( x = -48 + *ss ; isdigit ( * ++ ss ) ; ( x *= 10 ) += *ss - 48 ) ; ++ ss ;return opt ? x : -x ;}
# undef In_Len
}
using namespace In;
namespace Out
{# define Out_Len 2000000static std :: streambuf* fb ( std :: cout.rdbuf ( ) ) ;static char buf [Out_Len], *ss ( buf ) ;inline void write ( register int x )  {static int T [30], tp ( 0 ) ;if ( ! x )  {  *ss ++ =  48 ; *ss ++ = 10 ; return ;  }if ( x < 0 )  {  *ss ++ = 45 ; x = -x ;  }while ( x ) T [++ tp] = x % 10 | 48, x /= 10 ;while ( tp )  *ss ++ = T [tp --] ;*ss ++ = ' ' ;}inline void flush ( )  {  fb -> sputn ( buf, ss - buf ) ;  }
# undef Out_Len
}
using namespace Out;
const int MOD=998244353;
typedef long long ll;
inline int add(const int& x,const int& y){return x+y>=MOD? x+y-MOD:x+y;}
inline int dec(const int& x,const int& y){return x<y? x-y+MOD:x-y;}
inline int qpow(int a,int p)
{int ans=1;while (p){if (p&1) ans=(ll)ans*a%MOD;a=(ll)a*a%MOD;p>>=1;}return ans;
}
#define inv(x) qpow(x,MOD-2)
int rt[2][24];
int l,lim,r[MAXN];
inline void init(){lim=1<<l;for (int i=0;i<lim;i++) r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));}
inline void NTT(int* a,int type)
{for (int i=0;i<lim;i++) if (i<r[i]) swap(a[i],a[r[i]]);for (int L=0;L<l;L++){int mid=1<<L,len=mid<<1,Wn=rt[type][L+1];for (int s=0;s<lim;s+=len){ll w=1;for (int k=0;k<mid;k++,w=w*Wn%MOD){int x=a[s+k],y=w*a[s+mid+k]%MOD;a[s+k]=add(x,y);a[s+mid+k]=dec(x,y);}            }}if (type){int t=inv(lim);for (int i=0;i<lim;i++) a[i]=(ll)a[i]*t%MOD;}
}
void getinv(int* A,int* B,int n)
{static int f[MAXN],t[MAXN];if (n==1) return (void)(*B=inv(*A));getinv(A,t,(n+1)>>1);for (l=0;(1<<l)<(n<<1);l++);init();for (int i=0;i<n;i++) f[i]=A[i];for (int i=n;i<lim;i++) f[i]=t[i]=0;NTT(f,0);NTT(t,0);for (int i=0;i<lim;i++) B[i]=(ll)t[i]*dec(2,(ll)f[i]*t[i]%MOD)%MOD;NTT(B,1);for (int i=n;i<lim;i++) B[i]=0;
}
void getmod(int* A,int* B,int* R,int n,int m)
{static int F[MAXN],G[MAXN],H[MAXN],t[MAXN];for (int i=0;i<=n;i++) F[n-i]=A[i];for (int i=0;i<=m;i++) G[m-i]=B[i];getinv(G,t,n-m+1);for(l=0;(1<<l)<=(2*n-m);l++);init();for (int i=n+1;i<lim;i++) F[i]=0;for (int i=n-m+1;i<lim;i++) t[i]=0;NTT(F,0);NTT(t,0);for (int i=0;i<lim;i++) t[i]=(ll)F[i]*t[i]%MOD;NTT(t,1);for (int i=0;i<=n-m;i++) H[i]=t[n-m-i];for (int i=0;i<=m;i++) G[i]=B[i];for (l=0;(1<<l)<=n;l++);init();for (int i=n-m+1;i<lim;i++) H[i]=0;for (int i=m+1;i<lim;i++) G[i]=0;NTT(H,0);NTT(G,0);for (int i=0;i<lim;i++) t[i]=(ll)H[i]*G[i]%MOD;NTT(t,1);for (int i=0;i<m;i++) R[i]=dec(A[i],t[i]);
}
inline void deriv(int* A,int* B,int n){for (int i=0;i<n;i++) B[i]=(ll)A[i+1]*(i+1)%MOD;B[n]=0;}
int x[MAXN],y[MAXN];
#define lc p<<1
#define rc p<<1|1
int* g[MAXN];
void build(int p,int ql,int qr)
{g[p]=new int[qr-ql+2];if (ql==qr) return (void)(g[p][0]=MOD-x[ql],g[p][1]=1);int mid=(ql+qr)>>1;build(lc,ql,mid);build(rc,mid+1,qr);for(l=0;(1<<l)<=qr-ql+1;l++);init();int L[lim],R[lim];memset(L,0,sizeof(L));memset(R,0,sizeof(R));for (int i=0;i<=mid-ql+1;i++) L[i]=g[lc][i];for (int i=0;i<=qr-mid;i++) R[i]=g[rc][i];NTT(L,0);NTT(R,0);for (int i=0;i<lim;i++) L[i]=(ll)L[i]*R[i]%MOD;NTT(L,1);for (int i=0;i<=qr-ql+1;i++) g[p][i]=L[i];
}
int G[MAXN],val[MAXN],ans[MAXN];
void getval(int* F,int p,int ql,int qr,int* ans)//F:0...qr-ql
{if (qr-ql<=500){for (int i=ql;i<=qr;i++)for (int j=qr-ql;j>=0;j--)ans[i]=add((ll)ans[i]*x[i]%MOD,F[j]);return;}int mid=(ql+qr)>>1;int G[qr-ql+1];getmod(F,g[lc],G,qr-ql,mid-ql+1);getval(G,lc,ql,mid,ans);getmod(F,g[rc],G,qr-ql,qr-mid);getval(G,rc,mid+1,qr,ans);
}
void solve(int p,int ql,int qr,int* ans)
{if (ql==qr) return (void)(*ans=y[ql]);int len,mid=(ql+qr)>>1;for(len=0;(1<<len)<=qr-ql;len++);int L1[1<<len],R1[1<<len],L2[1<<len],R2[1<<len];memset(L1,0,sizeof(L1));memset(R1,0,sizeof(R1));memset(L2,0,sizeof(L2));memset(R2,0,sizeof(R2));solve(lc,ql,mid,L1);solve(rc,mid+1,qr,R2);for (int i=0;i<=mid-ql+1;i++) L2[i]=g[lc][i];for (int i=0;i<=qr-mid;i++) R1[i]=g[rc][i];l=len;init();NTT(L1,0);NTT(R1,0);NTT(L2,0);NTT(R2,0);for (int i=0;i<lim;i++) ans[i]=add((ll)L1[i]*R1[i]%MOD,(ll)L2[i]*R2[i]%MOD);NTT(ans,1);
}
int main()
{time_t START=clock();freopen("test.in","r",stdin);freopen("test.out","w",stdout);In_init();rt[0][23]=qpow(3,119);rt[1][23]=inv(rt[0][23]);for (int i=22;i>=0;i--){rt[0][i]=(ll)rt[0][i+1]*rt[0][i+1]%MOD;rt[1][i]=(ll)rt[1][i+1]*rt[1][i+1]%MOD;}int n=read();for (int i=1;i<=n;i++) x[i]=read(),y[i]=read();build(1,1,n);deriv(g[1],G,n);getval(G,1,1,n,val);cerr<<(clock()-START)*1000/CLOCKS_PER_SEC;for (int i=1;i<=n;i++) y[i]=(ll)y[i]*inv(val[i])%MOD;solve(1,1,n,ans);for (int i=0;i<n;i++) write(ans[i]);flush();return 0;
}

Part 3.组合线

阶乘幂

xi‾=x(x+1)(x+2)...(x+i−1)=(x+i−1)!(x−1)!x^{\overline{i}}=x(x+1)(x+2)...(x+i-1)=\frac{(x+i-1)!}{(x-1)!}xi=x(x+1)(x+2)...(x+i−1)=(x−1)!(x+i−1)!​称为上升阶乘幂,简称上升幂

xi‾=x(x−1)(x−2)...(x−i+1)=x!(x−i)!x^{\underline{i}}=x(x-1)(x-2)...(x-i+1)=\frac{x!}{(x-i)!}xi​=x(x−1)(x−2)...(x−i+1)=(x−i)!x!​称为下降阶乘幂,简称下降幂

形如∑i=0naixi‾\sum_{i=0}^na_ix^{\overline{i}}i=0∑n​ai​xi称为上升幂多项式

∑i=0naixi‾\sum_{i=0}^na_ix^{\underline{i}}i=0∑n​ai​xi​称为下降幂多项式

指数生成函数(EGF)

∑i=0∞aii!xi\sum_{i=0}^{\infin}\frac{a_i}{i!}x^ii=0∑∞​i!ai​​xi

这样就和组合数那套理论扯上了关系

ai=1a_i=1ai​=1的EGF为exe^xex

另外有个奇妙的性质

设两个EGFf(x),g(x)f(x),g(x)f(x),g(x)

f(x)g(x)=(∑i=0∞fii!)(∑i=0∞gii!)f(x)g(x)=(\sum_{i=0}^\infin\frac{f_i}{i!})(\sum_{i=0}^\infin\frac{g_i}{i!})f(x)g(x)=(i=0∑∞​i!fi​​)(i=0∑∞​i!gi​​)

f(x)g(x)=∑k=0∞∑i=0kfii!gk−i(k−i)!xkf(x)g(x)=\sum_{k=0}^\infin\sum_{i=0}^k\frac{f_i}{i!}\frac{g_{k-i}}{(k-i)!}x^kf(x)g(x)=k=0∑∞​i=0∑k​i!fi​​(k−i)!gk−i​​xk

=∑k=0∞(∑i=0kfigk−iCki)xkk!=\sum_{k=0}^\infin(\sum_{i=0}^kf_ig_{k-i}C_k^i)\frac{x^k}{k!}=k=0∑∞​(i=0∑k​fi​gk−i​Cki​)k!xk​

也就是说,EGF的乘法相当于系数卷起来再加一个组合数

相当于带了一个标号

下降幂多项式乘法

设下降幂多项式为f(x)f(x)f(x)

设F(x)F(x)F(x)为f(x)f(x)f(x)的点值指数型生成函数,即

F(x)=∑i=0∞f(i)i!xiF(x)=\sum_{i=0}^{\infin}\frac{f(i)}{i!}x^iF(x)=i=0∑∞​i!f(i)​xi

对于一个下降幂单项式xn‾x^{\underline{n}}xn​,其点值的EGFEGFEGF为

∑i=n∞i!(i−n)!i!xi=∑i=n∞1(i−n)!xi\sum_{i=n}^{\infin}\frac{i!}{(i-n)!i!}x^i=\sum_{i=n}^{\infin}\frac{1}{(i-n)!}x^ii=n∑∞​(i−n)!i!i!​xi=i=n∑∞​(i−n)!1​xi

提一个xnx^nxn出来

∑i=0∞1i!xn=exxn\sum_{i=0}^{\infin}\frac{1}{i!} x^n=e^xx^ni=0∑∞​i!1​xn=exxn

而xnx^nxn是它的系数普通型生成函数

什么意思呢?

设G(x)G(x)G(x)为f(x)f(x)f(x)的系数普通型生成函数

那么有

F(x)=exG(x)F(x)=e^xG(x)F(x)=exG(x)

(注意无论哪种生成函数都是普通多项式)

G(x)G(x)G(x)是给定的,叉上exe^xex再算上阶乘就可以得到点值,O(n)O(n)O(n)乘起来

同时

G(x)=e−xF(x)G(x)=e^{-x}F(x)G(x)=e−xF(x)

再乘回来就可以了

第二类斯特林数

定义:SnmS_n^mSnm​表示nnn个不同元素放入mmm个相同的非空集合的方案数

递推式:

Snm=Sn−1m−1+mSn−1mS_n^m=S_{n-1}^{m-1}+mS_{n-1}^mSnm​=Sn−1m−1​+mSn−1m​

即:每来一个新元素

  1. 新开一个集合
  2. 放入已有的mmm个集合

普及组难度

如何求通项公式?

假设集合可以为空,方案数为mnm^nmn

现在尝试用第二类斯特林数表示这玩意

我们枚举在哪些集合放了这nnn个元素,一个组合数就可以了。因为左边可以换顺序,而斯特林数是无序的,所以再乘一个阶乘。

mn=∑i=0mSnii!Cmim^n=\sum_{i=0}^{m}S_n^ii!C_m^imn=i=0∑m​Sni​i!Cmi​

二项式反演一波

f(m)=mn,g(i)=Snii!f(m)=m^n,g(i)=S_ n^ii!f(m)=mn,g(i)=Sni​i!

f(m)=∑i=0mCmig(i)f(m)=\sum_{i=0}^mC_m^ig(i)f(m)=i=0∑m​Cmi​g(i)

g(m)=∑i=0m(−1)m−iCmif(i)g(m)=\sum_{i=0}^m(-1)^{m-i}C_m^if(i)g(m)=i=0∑m​(−1)m−iCmi​f(i)

Snmm!=∑i=0m(−1)m−iCmiinS_ n^mm!=\sum_{i=0}^m(-1)^{m-i}C_m^ii^nSnm​m!=i=0∑m​(−1)m−iCmi​in

Snm=1m!∑i=0m(−1)m−iCmiinS_ n^m=\frac{1}{m!}\sum_{i=0}^m(-1)^{m-i}C_m^ii^nSnm​=m!1​i=0∑m​(−1)m−iCmi​in

得到了通项公式,但好像没啥用

我们把组合数拆开

Snm=1m!∑i=0m(−1)m−im!i!(m−i)!inS_ n^m=\frac{1}{m!}\sum_{i=0}^m(-1)^{m-i}\frac{m!}{i!(m-i)!}i^nSnm​=m!1​i=0∑m​(−1)m−ii!(m−i)!m!​in

发现可以约掉

Snm=∑i=0m(−1)m−i1i!(m−i)!inS_ n^m=\sum_{i=0}^m(-1)^{m-i}\frac{1}{i!(m-i)!}i^nSnm​=i=0∑m​(−1)m−ii!(m−i)!1​in

Snm=∑i=0mini!(−1)m−i(m−i)!S_ n^m=\sum_{i=0}^m\frac{i^n}{i!}\frac{(-1)^{m-i}}{(m-i)!}Snm​=i=0∑m​i!in​(m−i)!(−1)m−i​

我们发现这是个卷积形式

所以可以NTT求出第二类斯特林数的某一行(对于SnmS_n^mSnm​,nnn是行数,mmm是列数)


对第二类斯特林数的每一列构造生成函数Sm(x)S_m(x)Sm​(x)

由递推式

Snm=Sn−1m−1+mSn−1mS_n^m=S_{n-1}^{m-1}+mS_{n-1}^mSnm​=Sn−1m−1​+mSn−1m​

即:上一个右移再加上自己右移乘以一个系数

Sm(x)=xSm−1(x)+mxSm(x)S_m(x)=xS_{m-1}(x)+mxS_m(x)Sm​(x)=xSm−1​(x)+mxSm​(x)

Sm(x)=xSm−1(x)1−mxS_m(x)=\frac{xS_{m-1}(x)}{1-mx}Sm​(x)=1−mxxSm−1​(x)​

迭代下去

Sm(x)=xm∏i=1m(1−ix)S_m(x)=\frac{x^m}{\prod_{i=1}^m(1-ix)}Sm​(x)=∏i=1m​(1−ix)xm​

下面用分治NTT算出来求个逆乘上上面,就可以得到一列

第一类斯特林数

定义:snms_n^msnm​表示nnn个元素分成mmm个非空圆排列(即:旋转后相同视为相同)的方案数。

代表人物:0,6,11,60,6,11,60,6,11,6

递推式:

snm=sn−1m−1+(n−1)sn−1ms_n^m=s_{n-1}^{m-1}+(n-1)s_{n-1}^msnm​=sn−1m−1​+(n−1)sn−1m​

即每新来一个元素

  1. 新开一个圆排列
  2. 放在一个已有的元素之后

仍然是普及组难度

快速求法在后面讲

一些微妙的性质

xn=∑i=0nSnixi‾x^n=\sum_{i=0}^nS_n^ix^{\underline{i}}xn=i=0∑n​Sni​xi​

证明可以考虑归纳法

xn=x⋅xn−1=x∑i=0n−1Sn−1ixi‾=∑i=0n−1Sn−1ix⋅xi‾x^n=x·x^{n-1}=x\sum_{i=0}^{n-1}S_{n-1}^ix^{\underline i}=\sum_{i=0}^{n-1}S_{n-1}^ix·x^{\underline i}xn=x⋅xn−1=xi=0∑n−1​Sn−1i​xi​=i=0∑n−1​Sn−1i​x⋅xi​

冷静分析,

x⋅xi‾=x⋅x(x−1)(x−2)...(x−i+1)x·x^{\underline i}=x·x(x-1)(x-2)...(x-i+1)x⋅xi​=x⋅x(x−1)(x−2)...(x−i+1)

显然

xi+1‾=x(x−1)(x−2)...(x−i+1)(x−i)x^{\underline{i+1}}=x(x-1)(x-2)...(x-i+1)(x-i)xi+1​=x(x−1)(x−2)...(x−i+1)(x−i)

两式相减,得

x⋅xi‾=xi+1‾+i⋅xi‾x·x^{\underline i}=x^{\underline{i+1}}+i·x^{\underline i}x⋅xi​=xi+1​+i⋅xi​

代回去

xn=∑i=0n−1Sn−1i(xi+1‾+i⋅xi‾)x^n=\sum_{i=0}^{n-1}S_{n-1}^i(x^{\underline{i+1}}+i·x^{\underline i})xn=i=0∑n−1​Sn−1i​(xi+1​+i⋅xi​)

拆开

=∑i=0n−1Sn−1ixi+1‾+∑i=0n−1iSn−1ixi‾=\sum_{i=0}^{n-1}S_{n-1}^ix^{\underline{i+1}}+\sum_{i=0}^{n-1}iS_{n-1}^ix^{\underline i}=i=0∑n−1​Sn−1i​xi+1​+i=0∑n−1​iSn−1i​xi​

考虑把xi+1‾变成xi‾x^{\underline{i+1}}变成x^{\underline i}xi+1​变成xi​

改下求和项就可以了

=∑i=1nSn−1i−1xi‾+∑i=0n−1iSn−1ixi‾=\sum_{i=1}^nS_{n-1}^{i-1}x^{\underline i}+\sum_{i=0}^{n-1}iS_{n-1}^ix^{\underline i}=i=1∑n​Sn−1i−1​xi​+i=0∑n−1​iSn−1i​xi​

右边的000不要了,上面把nnn加上,这样可以写到一起

=∑i=1nSn−1i−1xi‾+∑i=1niSn−1ixi‾=\sum_{i=1}^nS_{n-1}^{i-1}x^{\underline i}+\sum_{i=1}^niS_{n-1}^ix^{\underline i}=i=1∑n​Sn−1i−1​xi​+i=1∑n​iSn−1i​xi​

合并起来

=∑i=1n(Sn−1i−1+iSn−1i)xi‾=\sum_{i=1}^n(S_{n-1}^{i-1}+iS_{n-1}^i)x^{\underline i}=i=1∑n​(Sn−1i−1​+iSn−1i​)xi​

发现了什么不得了的事情

=∑i=1nSnixi‾=\sum_{i=1}^nS_n^ix^{\underline i}=i=1∑n​Sni​xi​

类似的,我们可以得到

xn‾=∑i=0nsnixix^{\overline n}=\sum_{i=0}^ns_n^ix^ixn=i=0∑n​sni​xi

根据这个可以求出第一类斯特林数的一行

显然有个O(nlog⁡n2)O(n\log_n^2)O(nlogn2​)的分治NTT做法

倍增可以做到O(nlog⁡n)O(n\log n)O(nlogn)

考虑现在求x2n‾x^{\underline{2n}}x2n​

递归求出

f(x)=xn‾=∑i=0naixif(x)=x^{\underline n}=\sum_{i=0}^na_ix^if(x)=xn​=i=0∑n​ai​xi

显然x2n‾=xn‾(x+n)n‾x^{\underline{2n}}=x^{\underline n}(x+n)^{\underline n}x2n​=xn​(x+n)n​,所以我们只需要求出f(x+n)f(x+n)f(x+n)

f(x+n)=∑i=0nai(x+n)if(x+n)=\sum_{i=0}^na_i(x+n)^if(x+n)=i=0∑n​ai​(x+n)i

二项式定理拆开

f(x+n)=∑i=0nai∑j=0iCijxjni−jf(x+n)=\sum_{i=0}^na_i\sum_{j=0}^iC_i^jx^jn^{i-j}f(x+n)=i=0∑n​ai​j=0∑i​Cij​xjni−j

换个顺序

f(x+n)=∑j=0nxj∑i=jnaiCijni−jf(x+n)=\sum_{j=0}^nx^j\sum_{i=j}^ na_iC_i^jn^{i-j}f(x+n)=j=0∑n​xji=j∑n​ai​Cij​ni−j

把组合数也拆开

f(x+n)=∑j=0nxj∑i=jnaii!j!(i−j)!ni−jf(x+n)=\sum_{j=0}^nx^j\sum_{i=j}^ na_i\frac{i!}{j!(i-j)!}n^{i-j}f(x+n)=j=0∑n​xji=j∑n​ai​j!(i−j)!i!​ni−j

f(x+n)=∑j=0nxjj!∑i=jnaii!ni−j(i−j)!f(x+n)=\sum_{j=0}^n\frac{x^j}{j!}\sum_{i=j}^ na_ii!\frac{n^{i-j}}{(i-j)!}f(x+n)=j=0∑n​j!xj​i=j∑n​ai​i!(i−j)!ni−j​

把aaa翻一下

f(x+n)=∑j=0nxjj!∑i=jnan−i(n−i)!ni−j(i−j)!f(x+n)=\sum_{j=0}^n\frac{x^j}{j!}\sum_{i=j}^ na_{n-i}(n-i)!\frac{n^{i-j}}{(i-j)!}f(x+n)=j=0∑n​j!xj​i=j∑n​an−i​(n−i)!(i−j)!ni−j​

发现右边是个卷积 设卷出来是bbb

f(x+n)=∑j=0nxjj!bn−jf(x+n)=\sum_{j=0}^n\frac{x^j}{j!}b_{n-j}f(x+n)=j=0∑n​j!xj​bn−j​

再把bbb翻一下

f(x+n)=∑j=0nxjj!bjf(x+n)=\sum_{j=0}^n\frac{x^j}{j!}b_jf(x+n)=j=0∑n​j!xj​bj​

就可以求出来

当然如果nnn为奇数就手动乘上(x+n−1)(x+n-1)(x+n−1)


如何求一列?

我们发现一列的意义是mmm个圆排列凑出若干点的方案,考虑生成函数

因为有标号,考虑构造EGF

首先如果是一个圆排列,方案数是(n−1)!(n-1)!(n−1)!

其EGF为

f(x)=∑i=1∞(n−1)!n!xif(x)=\sum_{i=1}^\infin\frac{(n-1)!}{n!}x^if(x)=i=1∑∞​n!(n−1)!​xi

=∑i=1∞1ixi=\sum_{i=1}^\infin\frac{1}{i}x^i=i=1∑∞​i1​xi

mmm个圆排列就是mmm个f(x)f(x)f(x)乘起来,因为圆排列之间没有顺序,所以除以一个阶乘

fm(x)m!\frac{f^m(x)}{m!}m!fm(x)​

f(x)f(x)f(x)没有常数项,所以需要平移一位

下降幂多项式转普通多项式

因为上面已经证明过下降幂多项式的点值的 EGF 等于它的系数的 OGF 叉上exe^xex

所以算出点值跑快速插值就可以了。

点值连续,不用写多点求值,还是比较清真的。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cctype>
#define MAXN 524288
using namespace std;
const int MOD=998244353;
typedef long long ll;
inline int read()
{int ans=0;char c=getchar();while (!isdigit(c)) c=getchar();while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();return ans;
}
inline int add(const int& x,const int& y){return x+y>=MOD? x+y-MOD:x+y;}
inline int dec(const int& x,const int& y){return x<y? x-y+MOD:x-y;}
inline int qpow(int a,int p)
{int ans=1;while (p){if (p&1) ans=(ll)ans*a%MOD;a=(ll)a*a%MOD;p>>=1;}return ans;
}
#define inv(x) qpow(x,MOD-2)
int rt[2][24],fac[MAXN],finv[MAXN];
int l,r[MAXN],lim;
inline void init(){lim=1<<l;for (int i=0;i<lim;i++) r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));}
inline void NTT(int* a,int type)
{for (int i=0;i<lim;i++) if (i<r[i]) swap(a[i],a[r[i]]);for (int L=0;L<l;L++){int mid=1<<L,len=mid<<1;ll Wn=rt[type][L+1];for (int s=0;s<lim;s+=len)for (int k=0,w=1;k<mid;k++,w=w*Wn%MOD){int x=a[s+k],y=(ll)w*a[s+mid+k]%MOD;a[s+k]=add(x,y),a[s+mid+k]=dec(x,y);}}if (type){int t=inv(lim);for (int i=0;i<lim;i++) a[i]=(ll)a[i]*t%MOD;}
}
int F[MAXN],G[MAXN];
int* g[MAXN<<1];
#define lc p<<1
#define rc p<<1|1
void build(int p,int ql,int qr)
{g[p]=new int[qr-ql+2];if (ql==qr) return (void)(g[p][0]=MOD-ql,g[p][1]=1);int mid=(ql+qr)>>1;build(lc,ql,mid);build(rc,mid+1,qr);for (l=0;(1<<l)<=(qr-ql+1);l++);init();int L[lim],R[lim];memset(L,0,sizeof(L));memset(R,0,sizeof(R));for (int i=0;i<=mid-ql+1;i++) L[i]=g[lc][i];for (int i=0;i<=qr-mid;i++) R[i]=g[rc][i];NTT(L,0);NTT(R,0);for (int i=0;i<lim;i++) L[i]=(ll)L[i]*R[i]%MOD;NTT(L,1);for (int i=0;i<=qr-ql+1;i++) g[p][i]=L[i];
}
void solve(int p,int* y,int* ans,int ql,int qr)
{if (ql==qr) return (void)(*ans=y[ql]);int mid=(ql+qr)>>1;for (l=0;(1<<l)<=(qr-ql);l++);lim=1<<l;int len=l;int fl[lim],fr[lim],gl[lim],gr[lim];memset(fl,0,sizeof(fl));memset(fr,0,sizeof(fr));memset(gl,0,sizeof(gl));memset(gr,0,sizeof(gr));solve(lc,y,fl,ql,mid);solve(rc,y,fr,mid+1,qr);for (int i=0;i<=mid-ql+1;i++) gl[i]=g[lc][i];for (int i=0;i<=qr-mid;i++) gr[i]=g[rc][i];l=len,init();   NTT(fl,0);NTT(fr,0);NTT(gl,0);NTT(gr,0);for (int i=0;i<lim;i++) ans[i]=add((ll)fl[i]*gr[i]%MOD,(ll)fr[i]*gl[i]%MOD);NTT(ans,1);
}
int main()
{rt[0][23]=qpow(3,119);rt[1][23]=inv(rt[0][23]);for (int i=22;i>=0;i--){rt[0][i]=(ll)rt[0][i+1]*rt[0][i+1]%MOD;rt[1][i]=(ll)rt[1][i+1]*rt[1][i+1]%MOD;                       }fac[0]=1;for (int i=1;i<MAXN;i++) fac[i]=(ll)fac[i-1]*i%MOD;finv[MAXN-1]=inv(fac[MAXN-1]);for (int i=MAXN-2;i>=0;i--) finv[i]=(ll)finv[i+1]*(i+1)%MOD;int n=read();for (int i=0;i<n;i++) F[i]=read();for (int i=0;i<n;i++) G[i]=finv[i];for(l=0;(1<<l)<(n<<1);l++);init();NTT(F,0);NTT(G,0);for (int i=0;i<(1<<l);i++) F[i]=(ll)F[i]*G[i]%MOD;NTT(F,1);for (int i=0;i<n;i++) F[i]=(ll)F[i]*(((n-i-1)&1)? MOD-finv[n-i-1]:finv[n-i-1])%MOD;for (int i=n;i<(1<<l);i++) F[i]=0;build(1,0,n-1);solve(1,F,G,0,n-1);for (int i=0;i<n;i++) printf("%d ",G[i]);return 0;
}

Part 4.娱乐线

开根号

给定F(x)F(x)F(x)(常数项为111),求G(x)G(x)G(x)使得G2(x)≡F(x)(modxn)G^2(x) \equiv F(x) \pmod {x^n}G2(x)≡F(x)(modxn)。系数模998244353998244353998244353。

G2(x)≡F(x)(modxn)G^2(x) \equiv F(x) \pmod {x^n}G2(x)≡F(x)(modxn)

套路性地

G02(x)≡F(x)(modx⌈n2⌉)G_0^2(x) \equiv F(x) \pmod {x^{\lceil\frac{n}{2}\rceil}}G02​(x)≡F(x)(modx⌈2n​⌉)

G2(x)−G02(x)≡0(modx⌈n2⌉)G^2(x)-G_0^2(x) \equiv 0 \pmod {x^{\lceil\frac{n}{2}\rceil}}G2(x)−G02​(x)≡0(modx⌈2n​⌉)

(G(x)+G0(x))(G(x)−G0(x))≡0(modx⌈n2⌉)(G(x)+G_0(x))(G(x)-G_0(x)) \equiv 0 \pmod {x^{\lceil\frac{n}{2}\rceil}}(G(x)+G0​(x))(G(x)−G0​(x))≡0(modx⌈2n​⌉)

假装前面不为000

G(x)−G0(x)≡0(modx⌈n2⌉)G(x)-G_0(x) \equiv 0 \pmod {x^{\lceil\frac{n}{2}\rceil}}G(x)−G0​(x)≡0(modx⌈2n​⌉)

套路性地平方

G2(x)−2G(x)G0(x)+G02(x)≡0(modxn)G^2(x)-2G(x)G_0(x)+G^2_0(x) \equiv 0 \pmod {x^n}G2(x)−2G(x)G0​(x)+G02​(x)≡0(modxn)

F(x)−2G(x)G0(x)+G02(x)≡0(modxn)F(x)-2G(x)G_0(x)+G^2_0(x) \equiv 0 \pmod {x^n}F(x)−2G(x)G0​(x)+G02​(x)≡0(modxn)

G(x)≡G02(x)+F(x)2G0(x)(modxn)G(x) \equiv \frac{G_0^2(x)+F(x)}{2G_0(x)} \pmod {x^n}G(x)≡2G0​(x)G02​(x)+F(x)​(modxn)

求个逆就可以了

边界为n=1n=1n=1时,常数项为1=1\sqrt1=11​=1

实际上不为111也可以做,写个二次剩余就好了(D区)

void getsqrt(int *A,int* B,int n)
{if (n==1) return (void)(*B=1);getsqrt(A,B,(n+1)>>1);getinv(B,g,n);int l=0;while ((1<<l)<(n<<1)) ++l;init(l);int t=inv(2);for (int i=0;i<n;i++) f[i]=A[i],g[i]=(ll)g[i]*t%MOD;for (int i=n;i<(1<<l);i++) B[i]=f[i]=g[i]=0;NTT(B,l,1);NTT(f,l,1);NTT(g,l,1);for (int i=0;i<(1<<l);i++) B[i]=((ll)B[i]*B[i]%MOD+f[i])%MOD*g[i]%MOD;NTT(B,l,-1);for (int i=n;i<(1<<l);i++) B[i]=0;
}

你非要写ln+exp我也不拦你

多项式三角函数

给定F(x)F(x)F(x)(常数项为0),求G(x)≡sin(F(x))(modxn)G(x) \equiv sin(F(x)) \pmod{x^n}G(x)≡sin(F(x))(modxn)或G(x)≡cos(F(x))(modxn)G(x) \equiv cos(F(x)) \pmod{x^n}G(x)≡cos(F(x))(modxn)

欧拉公式

eiα=cosα+isinαe^{i \alpha}=cos\alpha+isin\alphaeiα=cosα+isinα

当α=π\alpha =\piα=π,就是传说中的eiπ=−1e^{i\pi}=-1eiπ=−1

把上面的α\alphaα取负

e−iα=cos(−α)+isin(−α)e^{-i \alpha}=cos(-\alpha)+isin(-\alpha)e−iα=cos(−α)+isin(−α)

e−iα=cos(α)−isin(α)e^{-i \alpha}=cos(\alpha)-isin(\alpha)e−iα=cos(α)−isin(α)

根据小学奥数和差问题

sinα=eiα−e−iα2isin\alpha=\frac{e^{i\alpha}-e^{-i\alpha}}{2i}sinα=2ieiα−e−iα​

cosα=eiα+e−iα2cos\alpha=\frac{e^{i\alpha}+e^{-i\alpha}}{2}cosα=2eiα+e−iα​

α\alphaα换成F(x)F(x)F(x),套exp⁡\expexp板子即可

int c[MAXN],x[MAXN],y[MAXN];
void getsin(int* A,int *B,int n)
{for (int i=0;i<n;i++) c[i]=(ll)A[i]*I%MOD;getexp(c,x,n);getinv(x,y,n);int t=inv(I*2ll%MOD);for (int i=0;i<n;i++){B[i]=x[i]-y[i];if (B[i]<0) B[i]+=MOD;B[i]=(ll)B[i]*t%MOD;}
}
void getcos(int* A,int *B,int n)
{for (int i=0;i<n;i++) c[i]=(ll)A[i]*I%MOD;getexp(c,x,n);getinv(x,y,n);int t=inv(2);for (int i=0;i<n;i++){B[i]=x[i]+y[i];if (B[i]>=MOD) B[i]-=MOD;B[i]=(ll)B[i]*t%MOD;}
}

反三角、双曲函数就是背公式套板子的事,就不讲了。(懒)

多项式全家桶学习笔记【持续更新】相关推荐

  1. 重拾CCNA,学习笔记持续更新ing......(4)

    重拾CCNA,学习笔记持续更新ing......(4) 路由器作用功能的经典解说(笑)(非原创) 假设你的名字叫小不点,你住在一个大院子里,你的邻居有很多小伙伴,在门口传达室还有个看大门的李大爷,李大 ...

  2. 【学习笔记】React+React全家桶学习笔记

    文章目录 1 为什么要使用React 2 React的定义 3 React的三大特性 4 React入门 4.1 hello_react 4.2 虚拟DOM的创建 4.3 JSX 4.4 模块与组件, ...

  3. vue全家桶学习笔记

    大纲 核心成员 vue:核心功能-插件,组件,数据管理,生命周期 vue-router:路由机制-拆分,拆开,将文件划分大小 vuex:vue官方提供的数据共享机制,全局性管理数据 编译打包 webp ...

  4. Admin.NET管理系统(vue3等前后端分离)学习笔记--持续更新

    我的学习笔记 - 9iAdmin.NET 欢迎学习交流 (一)前端笔记 1.1 关于.env的设置 1.2 关于路由模式问题 1.3 关于 vue.config.ts 1.4 关于 打包(pnpm r ...

  5. JS逆向学习笔记 - 持续更新中

    JS逆向学习笔记 寻找深圳爬虫工作,微信:cjh-18888 文章目录 JS逆向学习笔记 一. JS Hook 1. JS HOOK 原理和作用 原理:替换原来的方法. (好像写了句废话) 作用: 可 ...

  6. 这是我见过最完整的Spring全家桶学习笔记,没有之一!

    耗时将近一个月,终于赶在金九银十,学完了Spring全家桶系列.梳理了下整个过程,总结了一套经验和学习方法,分享给有需要的各位. 首先说一下,为什么我要花这么多时间,下功夫来恶补Spring全家桶? ...

  7. Vue全家桶学习笔记_零基础入门到入坑:Vue篇

    文章目录 前言 什么是Vue,什么又是框架 完善的准备 Vue 引入 npm 安装 cnpm镜像加速器 安装 Vue-Cli脚手架 安装 webpack 安装 webpack-cli 安装 axios ...

  8. 专升本 计算机 公共课学习笔记(持续更新中...)

    计算机公共课学习笔记 第一章 计算机基础知识(30分) 1.计算机概述 计算机(Computer)的起源与发展 计算机(Computer)也称"电脑",是一种具有计算功能.记忆功能 ...

  9. Redux 进阶 - react 全家桶学习笔记(二)

    注:这篇是17年1月的文章,搬运自本人 blog... https://github.com/BuptStEve/... 零.前言 在上一篇中介绍了 Redux 的各项基础 api.接着一步一步地介绍 ...

最新文章

  1. SAP MM 公司间退货STO流程后勤部分简述
  2. jquery操作复选框(checkbox)的12个小技巧总结
  3. VS2013常用快捷方式
  4. kaggle房价预测特征意思_R语言实战:复杂数据处理和分析之Kaggle房价预测
  5. REUSE_ALV_GRID_DISPLAY事件子过程和cl_gui_grid类的事件对应关系
  6. Echart---多项柱状图-2D/H5
  7. 清空文件夹里面的所有文件和文件夹
  8. nvme装系统不能自引导_新买的固态硬盘用AHCI不能装系统,而用IDE却可以?问题就在这里...
  9. java-线程-用ThreadLocal类实现线程范围内的数据共享
  10. DHT 爬虫的学习记录
  11. 2022新版X站模板 二开苹果cms视频网站源码可封装app(学习教程)
  12. 【机器学习】ROC曲线和AUC面积
  13. iOS 微信8.0.11更新,新功能,新变化
  14. 大二web期末大作业——动漫海贼王(可以很好的应付老师的作业)
  15. python动物专家系统_动物识别专家系统 实验报告
  16. 用于说明卷积神经网络(ConvNet)的Python脚本
  17. JAVA将英文字母的大写字母转换为小写字母。
  18. 陌陌前端面试 - 凉面
  19. Kubernetes — 核心资源对象 — Namespace
  20. 《OSPF和IS-IS详解》一1.2 ARPANET

热门文章

  1. 这个小姐姐真的很火辣......
  2. 要男女朋友有什么用?
  3. 我的狗丢了,所以我能加你微信吗? | 今日最佳
  4. 每日一笑 | 实名举报校长拿两份工资!
  5. java 上传文件编码_(java)有什么办法把MultipartFile上传的文件转为utf-8的编码吗
  6. java2的7次方怎么表示_静态市盈率要怎么看?
  7. 关于java的项目_关于java项目与javaweb项目
  8. c++中stringstream_文史哲与艺术中的数学_智慧树章节答案
  9. oracle中的的instr,Oracle中instr函数使用方法
  10. mysql中如何计算党龄_党员的党龄如何计算?