Link
Luogu - https://www.luogu.org/problemnew/show/P4238
Description
已知
F
(
x
)
F(x)
F(x) 求次数最小的
G
(
x
)
(
m
o
d
p
=
r
2
k
+
1
)
G(x)\pmod{p=r2^k+1}
G(x)(modp=r2k+1) 满足
F
(
x
)
⊗
G
(
x
)
≡
1
(
m
o
d
x
n
)
F(x)\otimes G(x)\equiv1\pmod{x^n}
F(x)⊗G(x)≡1(modxn)
倍增。
设
f
i
(
x
)
≡
F
−
1
(
x
)
(
m
o
d
x
2
i
)
f_i(x)\equiv F^{-1}(x)\pmod{x^{2^{i}}}
fi(x)≡F−1(x)(modx2i) ,显然有
f
0
(
x
)
≡
C
(
m
o
d
x
)
f_0(x)\equiv C\pmod{x}
f0(x)≡C(modx) 。其中
C
C
C 为
F
(
x
)
F(x)
F(x) 的常数项系数。
假设已知
f
i
(
x
)
f_i(x)
fi(x) ,要求
f
i
+
1
(
x
)
f_{i+1}(x)
fi+1(x) 。
显然
f
i
(
x
)
≡
f
i
+
1
(
x
)
(
m
o
d
x
2
i
)
f_i(x)\equiv f_{i+1}(x)\pmod{x^{2^{i}}}
fi(x)≡fi+1(x)(modx2i)
模数凑上去到
x
2
i
+
1
x^{2^{i+1}}
x2i+1 需要怎么操作呢……
正常选手的思路:这肯定要移项啊,然后还出 0 0 0 了,又很好随便搞一搞
请跳过下面的废话(
实际上模数翻倍就是有效长度翻倍
而且上面那个式子代表着 f i + 1 ( x ) f_{i+1}(x) fi+1(x) 截掉后面一半就是 f i ( x ) f_i(x) fi(x)
容易想到的一种构造方法是全部同乘 x 2 i x^{2^i} x2i 但是引进的 x 2 i x^{2^i} x2i 不好处理
这种构造方法的实际含义就是瞎jb把两个多项式一起向后平移
为什么要这么搞??有猫饼
现在我们有俩多项式
f i f_i fi ■■■
f i + 1 ​ f_{i+1}\! fi+1 ■■■□□□
emmm 想想能够用这两个鬼东西得到什么不改变长度的等式关系
但是下边那个比上面那个多了一串 这个搞不了
那咋办 干脆搞一个右边是 0 0 0 的同余式?但是也不方便
而且要得到的东西里面怎么想也不会有把多项式按位对齐相乘吧。。
诶 但是左边两坨黑黑的东西一模一样 感觉可以消掉 相减一下
消完呢 干什么? 自己卷自己 什么都没了 左边的式子还在
f
i
+
1
(
x
)
−
f
i
(
x
)
≡
0
(
m
o
d
x
2
i
)
f_{i+1}(x)-f_i(x)\equiv0\pmod{x^{2^i}}
fi+1(x)−fi(x)≡0(modx2i)
[
f
i
+
1
(
x
)
−
f
i
(
x
)
]
2
≡
0
(
m
o
d
x
2
i
+
1
)
[f_{i+1}(x)-f_i(x)]^2\equiv0\pmod{x^{2^{i+1}}}
[fi+1(x)−fi(x)]2≡0(modx2i+1)
展开
f
i
+
1
2
(
x
)
−
2
f
i
(
x
)
f
i
+
1
(
x
)
+
f
i
2
(
x
)
≡
0
(
m
o
d
x
2
i
+
1
)
{f_{i+1}}^2(x)-2f_i(x)f_{i+1}(x)+{f_i}^2(x)\equiv0\pmod{x^{2^i+1}}
fi+12(x)−2fi(x)fi+1(x)+fi2(x)≡0(modx2i+1)
要把
f
i
+
1
(
x
)
f_{i+1}(x)
fi+1(x) 消剩一次挪到一边 ……然后呢 搞什么鬼 平方根??
其实不用,你想想这些
f
f
f 是
F
F
F 的逆(类似情况一定要牢记!!逆跟原式相消)
两边同乘
F
F
F 即可。
f
i
+
1
(
x
)
≡
2
f
i
(
x
)
−
f
i
2
(
x
)
F
(
x
)
(
m
o
d
x
2
i
+
1
)
f_{i+1}(x)\equiv2f_i(x)-f_i^2(x)F(x)\pmod{x^{2^i+1}}
fi+1(x)≡2fi(x)−fi2(x)F(x)(modx2i+1)
f
i
+
1
(
x
)
≡
f
i
(
x
)
∗
[
2
−
f
i
(
x
)
F
(
x
)
]
(
m
o
d
x
2
i
+
1
)
f_{i+1}(x)\equiv f_i(x)*[2-f_i(x)F(x)]\pmod{x^{2^i+1}}
fi+1(x)≡fi(x)∗[2−fi(x)F(x)](modx2i+1)
快乐倍增。
有一个细节。NTT里面处理Rev数组的时候,为什么是在
n
2
\frac{n}{2}
2n 长度下翻转?
原因是:我们是把长度
2
i
+
1
2^{i+1}
2i+1 的
f
i
(
x
)
f_{i}(x)
fi(x) 和
F
(
x
)
F(x)
F(x) 卷成一个长度为
2
i
+
2
2^{i+2}
2i+2 的东西
然后再砍掉后面一半
但是你要怎么砍呢?干脆简单一点,不让多余的部分影响到的话,
直接不管就不会把多余的东西卷起来了……下次用到那一块位置的时候清掉就好……
观察多项式求逆的过程还可以发现:一个多项式有没有逆元完全取决于其常数项是否有逆元
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<ctime>
#include<cctype>
using namespace std;
const int MOD = 998244353;
const int MAXN = 3e5+5;
const int ROOT = 3;
int n, a[MAXN], Wn[MAXN], G[MAXN], Rev[MAXN], Lim = 1;
inline int add(int a,int b) {a+=b;return a>=MOD?a-MOD:a;}
inline int sub(int a,int b) {a-=b;return a<0?a+MOD:a;}
inline int mul(int a,int b) {return 1LL*a*b%MOD;}
int qpowm(int a, int b, int p = MOD)
{
static int ret;
ret = 1;
while (b > 0)
{
if (b & 1) ret = mul(ret,a);
a = mul(a,a);
b >>= 1;
}
return ret;
}
#define ksm(a,b) qpowm(a,b)
inline int Adjust(const long long& x, const long long& p)
{
return (x>=p)?(x-p):x;
}
namespace Poly
{
int Log, Tem;
inline void Init(int n)
{
n >>= 1;
Log = 0, Tem = 1;
while (Tem <= n) Tem <<= 1, ++Log;
Wn[0] = 1;
}
void NTT(int *a, const int& n, const int& Type)
{
Init(n);
for (register int i = 1; i < n; ++i) Rev[i] = (Rev[i>>1]>>1)|((i&1)<<(Log-1));
for (register int i = 1; i < n; ++i) if (Rev[i] > i) swap(a[Rev[i]], a[i]);
for (register int Mid = 1, Len, Multi, Y, Cur; Mid < n; Mid <<= 1)
{
Len = Mid << 1;
Multi = qpowm(ROOT, (MOD-1) / Len * Type + MOD - 1, MOD);
for (register int i = 1; i < Mid; ++i) Wn[i] = mul(Wn[i-1], Multi);
for (register int Pos = 0; Pos < n; Pos += Len)
{
for (register int Sub = 0; Sub < Mid; ++Sub)
{
Cur = Pos + Sub;
Y = mul(a[Cur + Mid], Wn[Sub]);
a[Cur+Mid] = sub(a[Cur], Y);
a[Cur] = add(a[Cur], Y);
}
}
}
if (Type<0)
{
register int invn = qpowm(n, MOD-2, MOD);
for (register int i = 0; i < n; ++i) a[i] =mul(a[i], invn);
}
}
int tem[MAXN];
void Inv(int *a, int *inv, const int& n)
{
inv[0] = qpowm(a[0], MOD-2, MOD);
for (register int t, i = 2; i <= n; i <<= 1)
{
t = i << 1;
fill(inv + (i >> 1), inv + t, 0);
fill(tem + i, tem + t, 0);
copy(a, a + i, tem);
NTT(inv, t, 1);
NTT(tem, t, 1);
for (register int i = 0; i < t; ++i) inv[i] = mul(inv[i], sub(2, (1ll * tem[i] * inv[i]) % MOD));
NTT(inv, t, -1);
}
fill(inv + n, inv + (n<<1), 0);
}
}
inline void GetInput()
{
scanf("%d", &n);
for (register int i = 0; i < n; ++i) scanf("%d", &a[i]);
while (Lim <= n) Lim <<= 1;
}
inline void Output()
{
for (register int i = 0; i < n; ++i) printf("%d ", G[i]);
}
int main()
{
GetInput();
Poly::Inv(a, G, Lim);
Output();
return 0;
}