多项式求逆
对于多项式A(x)求B(x)使得
A
(
x
)
B
(
x
)
=
1
(
m
o
d
x
n
)
A(x)B(x)=1(modx^n)
A(x)B(x)=1(modxn)
考虑倍增,设
B
0
(
x
)
B_0(x)
B0(x)使得
A
(
x
)
B
0
(
x
)
=
1
(
m
o
d
x
n
/
2
)
A(x)B_0(x)=1(mod x^{n/2})
A(x)B0(x)=1(modxn/2)
显然B(x)在
m
o
d
x
n
/
2
mod x^{n/2}
modxn/2的情况下也成立。(由此可见
B
0
(
x
)
B_0(x)
B0(x)和
B
(
x
)
B(x)
B(x)后
n
2
\dfrac{n}{2}
2n项相同)
A
(
x
)
B
0
(
x
)
=
A
(
x
)
B
(
x
)
(
m
o
d
x
n
/
2
)
A(x)B_0(x)=A(x)B(x)(modx^{n/2})
A(x)B0(x)=A(x)B(x)(modxn/2)
B
0
(
x
)
−
B
(
x
)
=
0
(
m
o
d
x
n
/
2
)
B_0(x)-B(x)=0(modx^{n/2})
B0(x)−B(x)=0(modxn/2)
两边平方
B
0
(
x
)
2
+
B
(
x
)
2
−
2
B
0
(
x
)
B
(
x
)
=
0
(
m
o
d
x
n
)
B_0(x)^2+B(x)^2-2B_0(x)B(x)=0(modx^n)
B0(x)2+B(x)2−2B0(x)B(x)=0(modxn)
同乘A(x)
A
(
x
)
B
0
(
x
)
2
+
B
(
x
)
−
2
B
0
(
x
)
=
0
(
m
o
d
x
n
)
A(x)B_0(x)^2+B(x)-2B_0(x)=0(mod x^n)
A(x)B0(x)2+B(x)−2B0(x)=0(modxn)
B
(
x
)
=
2
B
0
(
x
)
−
A
(
x
)
B
0
(
x
)
2
(
m
o
d
x
n
)
B(x)=2B_0(x)-A(x)B_0(x)^2(modx^n)
B(x)=2B0(x)−A(x)B0(x)2(modxn)
边倍增边维护B数组
注意在算
x
n
x^n
xn是要开到2*n的数组,避免循环卷积的影响。
void polyinv(ll *A,ll *B,int n)//已知A 求B A*B=1 mod x^n
{
if(n==1) {B[0]=pow_mod(A[0],mod-2);return;}
polyinv(A,B,n+1>>1);init(n-1<<1);//虽然不是严格2的幂次,当相当于上界松了,转移正确
for(int i=0;i<n;++i) ta[i]=A[i],tb[i]=B[i];
NTT(ta,1),NTT(tb,1);
for(int i=0;i<digit;++i) ta[i]=(2*tb[i]%mod-ta[i]*tb[i]%mod*tb[i]%mod+mod)%mod;
NTT(ta,-1);
for(int i=0;i<n;++i) B[i]=ta[i],ta[i]=tb[i]=0;
for(int i=n;i<digit;++i) B[i]=0,ta[i]=tb[i]=0;
}
多项式除法
已知n次多项式A(x),m次多项式B(x),求C(x),D(x)使得
A
(
x
)
=
B
(
x
)
C
(
x
)
+
D
(
x
)
A(x)=B(x)C(x)+D(x)
A(x)=B(x)C(x)+D(x)
可以看出C(x)为n-m次,D(x)为m-1次
考虑将x换为
1
x
\dfrac{1}{x}
x1,等式两边同乘
x
n
x^n
xn
x
n
A
(
1
x
)
=
x
m
B
(
1
x
)
x
n
−
m
C
(
1
x
)
+
x
n
D
(
1
x
)
x^nA(\dfrac{1}{x})=x^mB(\dfrac{1}{x})x^{n-m}C(\dfrac{1}{x})+x^nD(\dfrac{1}{x})
xnA(x1)=xmB(x1)xn−mC(x1)+xnD(x1)
对于一个n次多项式
x
n
A
(
1
x
)
x^nA(\dfrac{1}{x})
xnA(x1)相当于把系数翻转,记
A
′
(
x
)
A'(x)
A′(x)为翻转后的多项式
A
′
(
x
)
=
B
′
(
x
)
C
′
(
x
)
+
x
n
−
m
+
1
D
′
(
x
)
A'(x)=B'(x)C'(x)+x^{n-m+1}D'(x)
A′(x)=B′(x)C′(x)+xn−m+1D′(x)
由于
C
(
x
)
C(x)
C(x)是n-m次多项式所以在
m
o
d
x
n
−
m
+
1
modx^{n-m+1}
modxn−m+1的意义下不受影响
C
′
(
x
)
=
A
′
(
x
)
B
′
(
x
)
(
m
o
d
x
n
−
m
+
1
)
C'(x)=\dfrac{A'(x)}{B'(x)}(modx^{n-m+1})
C′(x)=B′(x)A′(x)(modxn−m+1)
多项式求逆,以上。
void polymod(ll *A,ll *B,ll *C,ll *D,int n,int m)//A = B *C +D
{
for(int i=0;i<=n-m;++i) tc[i]=A[n-i];
for(int i=0;i<=min(n-m,m);++i) td[i]=B[m-i];
polyinv(td,te,n-m+1);
init(n-m<<1);
NTT(tc,1);NTT(te,1);
for(int i=0;i<digit;++i) tc[i]=tc[i]*te[i]%mod;
NTT(tc,-1);
for(int i=0;i<=n-m;++i) tb[i]=C[i]=tc[n-m-i];
init(n);
for(int i=0;i<=m;++i) ta[i]=B[i];
NTT(ta,1);NTT(tb,1);
for(int i=0;i<digit;++i) ta[i]=ta[i]*tb[i]%mod;
NTT(ta,-1);
for(int i=0;i<m;++i) D[i]=(A[i]-ta[i]+mod)%mod;
for(int i=0;i<digit;++i) ta[i]=tb[i]=tc[i]=td[i]=te[i]=0;
}
多项式开根
求
B
(
x
)
2
=
A
(
x
)
(
m
o
d
x
n
)
B(x)^2=A(x)(modx^n)
B(x)2=A(x)(modxn)
考虑倍增
H
(
x
)
2
=
A
(
x
)
(
m
o
d
x
n
/
2
)
H(x)^2=A(x)(modx^{n/2})
H(x)2=A(x)(modxn/2)
移项后平方
(
H
(
x
)
2
−
A
(
x
)
)
2
=
0
(
m
o
d
x
n
)
(H(x)^2-A(x))^2=0(modx^n)
(H(x)2−A(x))2=0(modxn)
(
H
(
x
)
2
+
A
(
x
)
)
2
=
4
∗
H
(
x
)
2
A
(
x
)
(
m
o
d
x
n
)
(H(x)^2+A(x))^2=4*H(x)^2A(x)(modx^n)
(H(x)2+A(x))2=4∗H(x)2A(x)(modxn)
A
(
x
)
=
(
H
(
x
)
2
+
A
(
x
)
2
H
(
x
)
)
2
(
m
o
d
x
n
)
A(x)=(\dfrac{H(x)^2+A(x)}{2H(x)})^2(modx^n)
A(x)=(2H(x)H(x)2+A(x))2(modxn)
注意这里的
H
(
x
)
−
1
H(x)^{-1}
H(x)−1变成了mod(x^n)意义下的。
这里如果
A
[
0
]
=
1
,
B
[
0
]
=
1
A[0]=1,B[0]=1
A[0]=1,B[0]=1
否则要求二次剩余
int Sqrt(int x)
{
if(pow_mod(x,mod-1>>1)!=1) return 0;
ll a=rand();
while(pow_mod((a*a-x+mod)%mod,mod-1>>1)!=mod-1) a=rand();
O=(a*a-x+mod)%mod;
fs ans=(fs){1,0},as=(fs){a,1};
int b=mod+1>>1;
while(b)
{
if(b&1) ans=ans*as;
b>>=1;
as=as*as;
}
return ans.x;
}
void polysqrt(ll *A,ll *B,int n)
{
if(n==1)
{
if(A[0]==1) B[0]=1;
else B[0]=Sqrt(A[0]);
return ;
}
polysqrt(A,B,n+1>>1);init(n<<1);
polyinv(B,td,n);
for(int i=0;i<n;++i) tc[i]=A[i];
NTT(tc,1),NTT(td,1);
for(int i=0;i<digit;++i) tc[i]=tc[i]*td[i]%mod;
NTT(tc,-1);
ll inv2=(mod+1)/2;
for(int i=0;i<n;++i) B[i]=(tc[i]+B[i])%mod*inv2%mod,tc[i]=td[i]=0;
for(int i=n;i<digit;++i) B[i]=0,tc[i]=td[i]=0;
}
多项式多点求值
给定一个多项式
A
(
x
)
A(x)
A(x)和
x
1
−
x
n
x_1-x_n
x1−xn,求
A
(
x
)
A(x)
A(x)在这n个位置的值。
m
i
d
=
n
2
mid=\dfrac{n}{2}
mid=2n
令
A
0
(
x
)
=
∑
i
=
1
m
i
d
(
x
−
x
i
)
A_0(x)=\sum_{i=1}^{mid}(x-x_i)
A0(x)=∑i=1mid(x−xi),
A
1
(
x
)
=
∑
i
=
m
i
d
+
1
n
(
x
−
x
i
)
A_1(x)=\sum_{i=mid+1}^{n}(x-x_i)
A1(x)=∑i=mid+1n(x−xi)
考虑:
F
(
x
)
=
A
0
(
x
)
D
0
(
x
)
+
R
0
(
x
)
F(x)=A_0(x)D_0(x)+R_0(x)
F(x)=A0(x)D0(x)+R0(x)
F
(
x
)
=
A
1
(
x
)
D
1
(
x
)
+
R
1
(
x
)
F(x)=A_1(x)D_1(x)+R_1(x)
F(x)=A1(x)D1(x)+R1(x)
对于
i
<
=
m
i
d
i<=mid
i<=mid
F
(
x
i
)
=
A
0
(
x
i
)
D
0
(
x
i
)
+
R
0
(
x
i
)
F(x_i)=A_0(x_i)D_0(x_i)+R_0(x_i)
F(xi)=A0(xi)D0(xi)+R0(xi),而
A
0
(
x
i
)
A_0(x_i)
A0(xi)一定存在一项为0,所以
F
(
x
i
)
=
R
0
(
x
i
)
F(x_i)=R_0(x_i)
F(xi)=R0(xi),这样我们成功将次数减小一半。
i
>
m
i
d
i>mid
i>mid同理,这样我们成功将次数/2,递归下去。
A ( x ) A(x) A(x)需要先分治求出保存下来。
每一层需要一次多项式取模,复杂度 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n).
void qzinit(int rt,int l,int r)//预处理l~r的多项式(x-xi)
{
if(l==r)
{
P[rt]=new ll[2];
P[rt][0]=mod-X[l];
P[rt][1]=1;
return ;
}
int mid=l+r>>1;
qzinit(rt<<1,l,mid);qzinit(rt<<1|1,mid+1,r);
init(r-l+2);
for(int i=0;i<=mid-l+1;++i) ta[i]=P[rt<<1][i];
for(int i=0;i<=r-mid;++i) tb[i]=P[rt<<1|1][i];
P[rt]=new ll[r-l+2];
NTT(ta,1);NTT(tb,1);
for(int i=0;i<digit;++i) ta[i]=ta[i]*tb[i]%mod;
NTT(ta,-1); //ta,tb清零
for(int i=0;i<digit;++i)
{
if(i<=r-l+1) P[rt][i]=ta[i];
ta[i]=tb[i]=0;
}
}
void polyqz(int rt,int l,int r,int dep)
{
if(l==r)
{
as[l]=T[dep][0];
return ;
}
int mid=l+r>>1;
polymod(T[dep],P[rt<<1],ever,T[dep+1],r-l,mid-l+1);
polyqz(rt<<1,l,mid,dep+1);
polymod(T[dep],P[rt<<1|1],ever,T[dep+1],r-l,r-mid);
polyqz(rt<<1|1,mid+1,r,dep+1);
}
多项式求LN
注:只有常数项为1的多项式可以取ln.
l n F = ∫ F F − 1 lnF=\int_{}{}FF^{-1} lnF=∫FF−1
void direv(ll *A,int n)
{
for(int i=1;i<=n;++i) A[i-1]=A[i]*i%mod;A[n]=0;
}
void inter(ll *A,int n)
{
for(int i=n;i>=1;--i) A[i]=A[i-1]*pow_mod(i,mod-2)%mod;A[0]=0;
}
void polyln(ll *A,ll *B,int n) //mod x^n lnA=B
{
polyinv(A,B,n);direv(A,n-1);
init(n<<1);
for(int i=0;i<n;++i) tt[i]=A[i];
NTT(tt,1),NTT(B,1);
for(int i=0;i<digit;++i) B[i]=B[i]*tt[i]%mod;
NTT(B,-1);
for(int i=0;i<digit;++i) tt[i]=0;
inter(B,n-1);
}
多项式牛顿迭代
参考资料
已知
G
(
x
)
G(x)
G(x),求F(x)使得
G
(
F
(
x
)
)
=
0
G(F(x))=0
G(F(x))=0
m
o
d
x
n
mod x^n
modxn
考虑倍增
F
(
x
)
=
F
0
(
x
)
−
G
(
F
0
(
x
)
)
G
′
(
F
0
(
x
)
)
F(x)=F_{0}(x)-\dfrac{G(F_{0}(x))}{G'(F_{0}(x))}
F(x)=F0(x)−G′(F0(x))G(F0(x))
可以利用这个式子推导很多东西。
多项式exp
求
B
(
x
)
=
e
A
(
x
)
B(x)=e^{A(x)}
B(x)=eA(x)
m
o
d
x
n
mod x^n
modxn,保证A(0)=0。
l
n
F
(
x
)
−
A
(
x
)
=
0
lnF(x)-A(x)=0
lnF(x)−A(x)=0
l
n
F
(
x
)
−
A
(
x
)
=
G
(
F
(
x
)
)
lnF(x)-A(x)=G(F(x))
lnF(x)−A(x)=G(F(x))
两边求导
G
′
(
F
(
x
)
)
=
1
F
(
x
)
G'(F(x))=\dfrac{1}{F(x)}
G′(F(x))=F(x)1
带入牛顿迭代的式子
F
(
x
)
=
F
0
(
x
)
(
1
−
l
n
F
0
(
x
)
+
A
(
x
)
)
F(x)=F_{0}(x)(1-lnF_{0}(x)+A(x))
F(x)=F0(x)(1−lnF0(x)+A(x))
倍增即可
void polyexp(ll *A,ll *B,int n)
{
if(n==1)
{
B[0]=1;
return ;
}
polyexp(A,B,n+1>>1);init(n-1<<1);
for(int i=0;i<n;++i) tc[i]=td[i]=B[i];
polyln(td,te,n);
for(int i=0;i<n;++i) td[i]=(A[i]-te[i]+mod)%mod;
for(int i=n;i<digit;++i) td[i]=tc[i]=0;
td[0]++;
NTT(tc,1);NTT(td,1);
for(int i=0;i<digit;++i) tc[i]=tc[i]*td[i]%mod;
NTT(tc,-1);
for(int i=0;i<digit;++i)
{
if(i<n) B[i]=tc[i];
tc[i]=td[i]=te[i]=0;
}
}
多项式幂函数
求 G ( x ) = A ( x ) k G(x)=A(x)^k G(x)=A(x)k
如果A(0)=1
两边取ln
l
n
G
(
x
)
=
k
l
n
A
(
x
)
lnG(x)=klnA(x)
lnG(x)=klnA(x)
再两边变成e的指数
G
(
x
)
=
e
x
p
(
k
l
n
A
(
x
)
)
G(x)=exp(klnA(x))
G(x)=exp(klnA(x))
否则
设最低项为
a
t
x
t
a_tx^t
atxt,那我把多项式拆成两部分以便于上述操作
G
(
x
)
=
a
t
k
x
k
t
e
x
p
(
k
l
n
A
(
x
)
a
t
x
t
)
G(x)=a_t^kx^{kt}exp(kln\dfrac{A(x)}{a_tx^t})
G(x)=atkxktexp(klnatxtA(x))
void polypow(ll *A,ll *B,int n,ll k)
{
if(A[0]==1)//判常数项是否为1
{
polyln(A,C,n);
for(int i=0;i<n;++i) C[i]=C[i]*k%mod;
polyexp(C,B,n);
for(int i=0;i<n;++i) C[i]=0;
}
else
{
int pos=0;
for(;!A[pos];++pos);
int tmp=pow_mod(A[pos],mod-2);
for(int i=pos;i<n;++i) D[i-pos]=A[i]*tmp%mod;
polyln(D,C,n);
for(int i=0;i<n;++i) C[i]=C[i]*k%mod;
polyexp(C,B,n);
tmp=pow_mod(A[pos],k);
if(pos*k<n) for(int i=n-pos*k-1;i>=0;--i) B[i+pos*k]=B[i]*tmp%mod;
for(int i=0;i<n&&i<pos*k;++i) B[i]=0;
for(int i=0;i<n;++i) C[i]=D[i]=0;
}
}