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

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

最近更新于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(1kn)互不相同

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

显然就是原根

具体地说,w≡gMOD−1n(modMOD)w\equiv g^{\frac{MOD-1}{n}}\pmod {MOD}wgnMOD1(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×B1(mod xn),系数对998244353取模998244353取模998244353

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


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

两式相减

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

假装AAA不为0

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

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

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

BBB不爽,乘个AAA消掉

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

B≡2B′−AB′2(modxn)B\equiv2B'-AB'^2 \pmod {x^n}B2BAB2(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)=nxn1

积分是逆运算

∫f(x)dx=1n+1xn+1\int f(x)dx=\frac{1}{n+1}x^{n+1}f(x)dx=n+11xn+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=0i!f(i)(x0)(xx0)i

(f(i)f^{(i)}f(i)表示fffiii阶导数)

网上资料很多,不再细讲

正文

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(modx2n)

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)(modx2n)……

什么?为什么?

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

因为方程有唯一解(不然没有意义),所以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)(modx2n)

所以(G(x)−G0(x))2≡0(modxn)(G(x)-G_0(x))^2\equiv0\pmod {x^n}(G(x)G0(x))20(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)(1ln(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=1n(x+ai)

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

注意这个算法的核心并不是减少NTT次数(NTT次数仍然是n−1n-1n1)而是在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)=xnmQ(x1)xmG(x1)+xnm+1xm1R(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)+xnm+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)(modxnm+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,m64000

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

所以多半是分治

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

G(x)=∏LR(x−ai)G(x)=\prod_{L}^{R}(x-a_i)G(x)=LR(xai)
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-1n1次多项式

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=1nyii=jxixjxxj

分式看着很烦,把它拆开

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=1ni=j(xixj)yii=j(xxj)

考虑左边这坨怎么求

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

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

我们设

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

那么

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

可以写成

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

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

哎怎么分母是000

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

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

因为x→0x\to0x0的时候上下同时趋近于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=1ng(xi)yii=j(xxj)

我们先分治算出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=1nyii=j(xxj)

当然如果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=lryii=l,i=jr(xxj)

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(xxj)[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(xxi)即可

同理可得

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+i1)=(x1)!(x+i1)!称为上升阶乘幂,简称上升幂

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(x1)(x2)...(xi+1)=(xi)!x!称为下降阶乘幂,简称下降幂

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

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

指数生成函数(EGF)

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

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

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=0i!fi)(i=0i!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=0i=0ki!fi(ki)!gkixk

=∑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=0kfigkiCki)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=0i!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(in)!i!i!xi=i=n(in)!1xi

提一个xnx^nxn出来

∑i=0∞1i!xn=exxn\sum_{i=0}^{\infin}\frac{1}{i!} x^n=e^xx^ni=0i!1xn=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)=exF(x)

再乘回来就可以了

第二类斯特林数

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

递推式:

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

即:每来一个新元素

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

普及组难度

如何求通项公式?

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

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

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

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

二项式反演一波

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

f(m)=∑i=0mCmig(i)f(m)=\sum_{i=0}^mC_m^ig(i)f(m)=i=0mCmig(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=0m(1)miCmif(i)

Snmm!=∑i=0m(−1)m−iCmiinS_ n^mm!=\sum_{i=0}^m(-1)^{m-i}C_m^ii^nSnmm!=i=0m(1)miCmiin

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

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

我们把组合数拆开

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!1i=0m(1)mii!(mi)!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=0m(1)mii!(mi)!1in

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=0mi!in(mi)!(1)mi

我们发现这是个卷积形式

所以可以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=Sn1m1+mSn1m

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

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

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

迭代下去

Sm(x)=xm∏i=1m(1−ix)S_m(x)=\frac{x^m}{\prod_{i=1}^m(1-ix)}Sm(x)=i=1m(1ix)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=sn1m1+(n1)sn1m

即每新来一个元素

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

仍然是普及组难度

快速求法在后面讲

一些微妙的性质

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

证明可以考虑归纳法

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=xxn1=xi=0n1Sn1ixi=i=0n1Sn1ixxi

冷静分析,

x⋅xi‾=x⋅x(x−1)(x−2)...(x−i+1)x·x^{\underline i}=x·x(x-1)(x-2)...(x-i+1)xxi=xx(x1)(x2)...(xi+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(x1)(x2)...(xi+1)(xi)

两式相减,得

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

代回去

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=0n1Sn1i(xi+1+ixi)

拆开

=∑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=0n1Sn1ixi+1+i=0n1iSn1ixi

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

改下求和项就可以了

=∑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=1nSn1i1xi+i=0n1iSn1ixi

右边的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=1nSn1i1xi+i=1niSn1ixi

合并起来

=∑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=1n(Sn1i1+iSn1i)xi

发现了什么不得了的事情

=∑i=1nSnixi‾=\sum_{i=1}^nS_n^ix^{\underline i}=i=1nSnixi

类似的,我们可以得到

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

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

显然有个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=0naixi

显然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=0nai(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=0naij=0iCijxjnij

换个顺序

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=0nxji=jnaiCijnij

把组合数也拆开

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=0nxji=jnaij!(ij)!i!nij

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=0nj!xji=jnaii!(ij)!nij

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=0nj!xji=jnani(ni)!(ij)!nij

发现右边是个卷积 设卷出来是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=0nj!xjbnj

再把bbb翻一下

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

就可以求出来

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


如何求一列?

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

因为有标号,考虑构造EGF

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

其EGF为

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

=∑i=1∞1ixi=\sum_{i=1}^\infin\frac{1}{i}x^i=i=1i1xi

mmm个圆排列就是mmmf(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)(modx2n)

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(modx2n)

(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(modx2n)

假装前面不为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(modx2n)

套路性地平方

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)eiα=cos(α)+isin(α)

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

根据小学奥数和差问题

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

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

α\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;}
}

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/315259.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2021牛客第五场 I.Interval Queries-回滚莫队

如图&#xff1a;一道很裸的回滚莫队&#xff0c;注意加入的操作和回滚的操作就好了。 #include <cstdio> #include <cstring> #include <string> #include <cmath> #include <iostream> #include <algorithm> #include <queue> #i…

Linux下搭建asp.net运行环境

最近有个项目&#xff0c;是在Windows平台下开发的&#xff0c;需要把 asp.net web应用移植到 CentOS下&#xff0c;甚是头疼&#xff1b;翻阅资料&#xff0c;发现Jexus是个可行的方案&#xff0c;下面是官方对Jexus的定义&#xff1a;什么是JexusJexus是一款Linux平台上的高性…

Codeforces Global Round 14 F. Phoenix and Earthquake 思维 + 并查集

传送门 文章目录题意&#xff1a;思路&#xff1a;题意&#xff1a; 给你nnn个点&#xff0c;mmm条边&#xff0c;限制xxx&#xff0c;每个点都有沥青aia_iai​&#xff0c;定义合并两个点即两点之间有边且auav≥xa_ua_v\ge xau​av​≥x&#xff0c;合并之后的沥青为auav−xa…

Codeforces Round #736 (Div. 2)E. The Three Little Pigs-长除法求多项式系数

https://codeforces.com/contest/1549/problem/E 题目大意自己悟吧&#xff0c;不解释了。 这题在赛时我想的是fft&#xff0c;但是题解说不用fft就能求出多项式的系数&#xff0c;fft在这题时间复杂度有点高。 使用多项式长除法就能求出系数&#xff0c;k^x的系数就是抓x个p…

在 Asp.Net Core WebAPI 中防御跨站请求伪造攻击

什么是跨站请求伪造跨站请求伪造&#xff08;英语&#xff1a;Cross-site request forgery&#xff09;&#xff0c;也被称为 one-click attack 或者 session riding&#xff0c;通常缩写为 CSRF 或者 XSRF&#xff0c; 是一种挟制用户在当前已登录的Web应用程序上执行非本意的…

Codeforces Round #700 (Div. 1) C. Continuous City 构造 + 二进制

传送门 文章目录题意&#xff1a;思路&#xff1a;题意&#xff1a; 构造一个图&#xff0c;使其从111到nnn的路径的长度与[L,R][L,R][L,R]中某个值一一对应&#xff0c;不能有两条路径长度一样&#xff0c;且每个值都必须出现一次&#xff0c;每两个点之间只能连一条边。 n≤…

HDU2021多校第五天 1009 Array-树状数组求二阶前缀和

https://acm.hdu.edu.cn/showproblem.php?pid7020 题目大意&#xff1a;求出区间众数严格大于区间一半大小的子区间的数量。 思路&#xff1a;先对每一个数求出对应的位置排列&#xff0c;S[j]代表1-有多少个i&#xff0c;易得s[r]-s[l]>r-l-(s[r]-s[l])的时候子区间{l1&a…

C# 8 新增小功能

尽管 C# 8 应该会在今年发布&#xff0c;并且 C# 8.x 和 9 的路线图也开始形成&#xff0c;但是&#xff0c;微软正在继续审查下一个版本的特性。目标类型表达式假设我们有 A、B 和 C 三种类型&#xff0c;其中类型 B 和 C 是类型 A 的子类型。如果我们使用如下所示的语句&…

【SDOI2014】数表【莫比乌斯反演】【树状数组】

传送门 传送门 题意&#xff1a; TTT组询问给定n,m,an,m,an,m,a,求 ∑i1N∑j1M[(∑d∈N∗[d∣i][d∣j]d)≤a]∑d∈N∗[d∣i][d∣j]d\sum_{i1}^{N}\sum_{j1}^{M}[(\sum_{d \in N^*}[d\mid i][d \mid j]d)\leq a]\sum_{d \in N^*}[d\mid i][d \mid j]di1∑N​j1∑M​[(d∈N∗∑…

Codeforces Global Round 14 E. Phoenix and Computers 思维 + dp

传送门 文章目录题意&#xff1a;思路&#xff1a;题意&#xff1a; 有nnn台电脑&#xff0c;你可以手动打开某个电脑&#xff0c;如果第i−1,i1i-1,i1i−1,i1台电脑都打开了&#xff0c;那么第iii台电脑会自动打开。不能手动打开自动打开的电脑&#xff0c;问有多少种打开的方…

2021HDU多校第五场1004 Another String-双指针

https://acm.hdu.edu.cn/showproblem.php?pid7015 题意&#xff1a;对于每次以i来分割字符串&#xff0c;计算两个分割串的子串的k-匹配的数量。 思路&#xff1a;当我们计算到ans[i]是&#xff0c;我们可以当成从ans[i-1]加上【1-i】字符串和【i1&#xff0c;n】的k-匹配数量…

C# 默认接口方法更新完成,很多细节问题尚待解决

随着对默认接口方法的支持越来越接近完成&#xff0c;一些潜在的问题被提了出来。虽然已经完成了很多工作&#xff0c;但这是一个复杂的特性&#xff0c;许多细节问题还没有解决。但首先&#xff0c;这里有一些已解决的问题。接口允许使用 static 和 const 字段了。除 和! 之外…

【洛谷3768】简单的数学题【莫比乌斯反演】【杜教筛】【小学奥数】

传送门 题意&#xff1a;给定p,Np,Np,N,求 ∑i1N∑j1Nijgcd(i,j)modp\sum_{i1}^{N}\sum_{j1}^{N}ijgcd(i,j)\text{ }mod \text{ }pi1∑N​j1∑N​ijgcd(i,j) mod p ppp为质数&#xff0c;在1e91e91e9左右 N≤1e10N \leq 1e10N≤1e10 神仙题 前置芝士&#xff1a;杜教筛 懒得…

newcode Gene Tree 点分治

传送门 文章目录题意&#xff1a;思路&#xff1a;题意&#xff1a; 求一棵树的每对叶子节点之间距离平方的和。 思路&#xff1a; 这个题貌似可以容斥&#xff0c;但是我不会&#xff0c;所以我写了个淀粉质。 要知道&#xff0c;淀粉质的思想就是将子树内部的递归处理&…

2021HDU多校第九场1008HDU7073 Integers Have Friends 2. 随机化

HUD地址&#xff1a;https://acm.hdu.edu.cn/showproblem.php?pid7073 题目大意&#xff1a;选择数组中最多的数&#xff0c;使得他们模m同余&#xff08;m>2&#xff09;。求最大的数量。 思路&#xff1a;然后我们全选奇数或者全选偶数的话&#xff0c;那么我们的答案是…

一个引发程序员们干架的问题

这里是Z哥的个人公众号每周五早8点 按时送达当然了&#xff0c;也会时不时加个餐&#xff5e;我的第「77」篇原创敬上在一个分布式系统的开发团队中&#xff0c;有一些问题是很容易产生程序员之间矛盾的。其中之一就是「业务归属」&#xff0c;就是当新加/修改一个业务的时候&a…

newcode Islands 思维

传送门 文章目录题意&#xff1a;思路&#xff1a;题意&#xff1a; 给你两个圆&#xff0c;上面依次有nnn个点&#xff0c;编号为1−n1-n1−n的排列&#xff0c;给出一种连边方式&#xff0c;使得每个点都被遍历且连线不能相交&#xff0c;没有方式的话输出−1-1−1。 思路&…

微服务探索与实践—服务注册与发现

前言微服务从大规模使用到现在已经有很多年了&#xff0c;从之前的探索到一步步的不断完善与成熟&#xff0c;微服务已经成为众多架构选择中所必须面对的一个选项。服务注册与发现是相辅相成的&#xff0c;所以一般会合起来思索。其依托组件有很多&#xff0c;比如Zookeeper,Co…

快速沃尔什变换:从入门到背板(含推导过程)

前&#xff08;che&#xff09;言&#xff08;dan&#xff09; FWTFWTFWT是个神奇的东西。 然而网上多数讲解多数直接给结论&#xff0c;顶多用归纳法证一证。 所以本文会讲解FWTFWTFWT的推导过程。 虽然也用到了构造&#xff0c;但是好背得多 参考博客&#xff1a;https:/…

微服务探索与实践—总述

背景软件开发是一个不断发展的过程&#xff0c;从当初的面向过程为主到如今的面向对象的开发&#xff0c;软件开发者不断探索与实践更加符合时代发展要求的开发模式与架构思想&#xff0c;而这&#xff0c;也在极大程度上提高了软件开发的效率。微服务是一种架构模式或者说是架…