[Luogu P4239 P4239] 【模板】多项式求逆(加强版)

10 篇文章 0 订阅
7 篇文章 0 订阅
洛谷传送门(标准版)
洛谷传送门(加强版)

题目描述

给定一个多项式 F ( x ) F(x) F(x) ,请求出一个多项式 G ( x ) G(x) G(x) , 满足 F ( x ) ∗ G ( x ) ≡ 1 ( m o d   x n ) F(x) * G(x) \equiv 1 ( \mathrm{mod\:} x^n ) F(x)G(x)1(modxn) 。系数对 998244353 / 1 0 9 + 7 998244353/10^9+7 998244353/109+7 取模。

输入输出格式

输入格式:

首先输入一个整数 n n n, 表示输入多项式的次数。
接着输入 n n n 个整数,第 i i i 个整数 a i a_i ai​ 代表 F ( x ) F(x) F(x) 次数为 i − 1 i-1 i1 项的系数。

输出格式:

输出 n n n 个数字,第 i i i 个整数 b i b_i bi 代表 G ( x ) G(x) G(x) 次数为 i − 1 i-1 i1 的项的系数。

输入输出样例

输入样例#1:
5
1 6 3 4 9
输出样例#1:
1 1000000001 33 999999823 1020

说明

1 ≤ n ≤ 1 0 5 , 0 ≤ a i ≤ 1 0 9 1 \leq n \leq 10^5, 0 \leq a_i \leq 10^9 1n105,0ai109

解题分析

假设我们求出了 G ′ ( x ) G'(x) G(x)使得 F ( x ) ∗ G ′ ( x ) ≡ 1 ( m o d   x ⌈ n 2 ) ⌉ F(x)*G'(x)\equiv 1(mod\ x^{\lceil\frac{n}{2})\rceil} F(x)G(x)1(mod x2n) ,显然也有 F ( x ) ∗ G ( x ) ≡ 1 ( m o d   x ⌈ n 2 ) ⌉ F(x)*G(x)\equiv 1(mod\ x^{\lceil\frac{n}{2})\rceil} F(x)G(x)1(mod x2n)。 所以有:
G ( x ) − G ′ ( x ) ≡ 0 ( m o d   x ⌈ n 2 ⌉ ) G(x)-G'(x)\equiv 0(mod\ x^{\lceil\frac{n}{2} \rceil}) G(x)G(x)0(mod x2n)
平方一下就有:
G 2 ( x ) − 2 G ′ ( x ) G ( x ) + G ′ 2 ( x ) ≡ 0 ( m o d   x n ) G^2(x)-2G'(x)G(x)+G'^2(x)\equiv 0(mod\ x^n) G2(x)2G(x)G(x)+G2(x)0(mod xn)
乘上 F ( x ) F(x) F(x)就有:
G ( x ) − 2 G ′ ( x ) + F ( x ) G ′ 2 ( x ) ≡ 0 ( m o d   x n ) G(x)-2G'(x)+F(x)G'^2(x)\equiv 0(mod\ x^n) G(x)2G(x)+F(x)G2(x)0(mod xn)
显然就可以递归先算出 G ′ ( x ) G'(x) G(x), 再做两次多项式乘法即可。

边界条件: n = 1 n=1 n=1 G ′ ( x ) = i n v ( F [ 0 ] ) G'(x)=inv(F[0]) G(x)=inv(F[0])

代码如下:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <algorithm>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define ll long long
#define MOD 998244353
#define G 3
#define Ginv 332748118
#define MX 400500
template <class T>
IN void in(T &x)
{
	x = 0; R char c = gc;
	for (; !isdigit(c); c = gc);
	for (;  isdigit(c); c = gc)
	x = (x << 1) + (x << 3) + c - 48;
}
int l;
int a[MX], b[MX], res[MX], rev[MX], ind[MX];
IN int fpow(R int base, R int tim)
{
	int ret = 1;
	W (tim)
	{
		if (tim & 1) ret = 1ll * ret * base % MOD;
		base = 1ll * base * base % MOD, tim >>= 1;
	}
	return ret;
}
IN void NTT(int *dat, R int typ, R int len)
{
	for (R int i = 0; i < len; ++i) if (rev[i] > i) std::swap(dat[i], dat[rev[i]]);
	R int seg, now, cur, step, bd, buf1, buf2, deal, base;
	for (seg = 1; seg < len; seg <<= 1)
	{
		base = fpow(typ ? G : Ginv, (MOD - 1) / (seg << 1)); step = seg << 1;
		for (now = 0; now < len; now += step)
		{
			deal = 1, bd = now + seg;
			for (cur = now; cur < bd; ++cur, deal = 1ll * deal * base % MOD)
			{
				buf1 = dat[cur], buf2 = 1ll * dat[cur + seg] * deal % MOD;
				dat[cur] = (buf1 + buf2) % MOD, dat[cur + seg] = (buf1 - buf2 + MOD) % MOD;
			}
		}
	}
	if (typ) return; int inv = fpow(len, MOD - 2);
	for (R int i = 0; i < len; ++i) dat[i] = 1ll * dat[i] * inv % MOD;
}
void getinv(R int up)
{
	if (up == 1) return res[0] = fpow(ind[0], MOD - 2), void();
	int half = up + 1 >> 1, len, lg; getinv(half);
	for (len = 1, lg = 0; len <= (up << 1); ++lg, len <<= 1);
	for (R int i = 1; i < len; ++i) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << lg - 1);
	for (R int i = 0; i < len; ++i) b[i] = a[i] = 0;
	for (R int i = 0; i < up; ++i) a[i] = ind[i];
	for (R int i = 0; i < half; ++i) b[i] = res[i];
	NTT(a, 1, len), NTT(b, 1, len);
	for (R int i = 0; i < len; ++i) a[i] = (2 * b[i] % MOD - 1ll * a[i] * b[i] % MOD * b[i] % MOD + MOD) % MOD;
	NTT(a, 0, len);
	for (R int i = 0; i < up; ++i) res[i] = a[i];
}
int main(void)
{
	in(l);
	for (R int i = 0; i < l; ++i) in(ind[i]);
	getinv(l);
	for (R int i = 0; i < l; ++i) printf("%d ", res[i]);
}

那么问题来了, 如果模数不为 N T T NTT NTT质数怎么办?

这里 F F T FFT FFT做了再取模的主要问题在于卷积大小可能达到 1 0 9 ∗ 1 0 9 ∗ 1 0 5 = 1 0 23 10^9*10^9*10^5=10^{23} 109109105=1023, 显然会爆 d o u b l e double double的精度。

有个很直接的想法: 把 x x x分成 k × x + p k\times \sqrt{x}+p k×x +p来做, 然后对于 k , p k,p k,p分开做 F F T FFT FFT, 然后合起来即可。

常数巨大, 接近 O ( N l o g 2 ( N ) ) O(Nlog^2(N)) O(Nlog2(N))

代码如下:

#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <algorithm>
#include <cstring>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define MX 400500
#define db long double
#define ll long long
#define MOD 1000000007ll
const int BASE = std::sqrt(MOD);
template <class T>
IN void in(T &x)
{
	x = 0; R char c = gc;
	for (; !isdigit(c); c = gc);
	for (;  isdigit(c); c = gc)
	x = (x << 1) + (x << 3) + c - 48;
}
int n;
int ans[MX], ind[MX], c1[MX], c2[MX], res[MX], rev[MX];
namespace Poly
{
	const db PI = std::acos(-1.0);
	struct Complex {db re, im;} a[MX], b[MX], c[MX], d[MX], w[MX];
	IN Complex operator * (const Complex &x, const Complex &y)
	{return {x.re * y.re - x.im * y.im, x.re * y.im + x.im * y.re};}
	IN Complex operator + (const Complex &x, const Complex &y)
	{return {x.re + y.re, x.im + y.im};}
	IN Complex operator - (const Complex &x, const Complex &y)
	{return {x.re - y.re, x.im - y.im};}
	IN int fpow(R int base, R int tim)
	{
		int ret = 1;
		W (tim)
		{
			if (tim & 1) ret = 1ll * ret * base % MOD;
			base = 1ll * base * base % MOD, tim >>= 1;
		}
		return ret;
	}
	IN void FFT(Complex *dat, R int len, R int typ)
	{
		for (R int i = 1; i < len; ++i) if (rev[i] > i) std::swap(dat[rev[i]], dat[i]);
		R int cur, now, seg, bd, step, tag, id; Complex buf1, buf2;
		for (seg = 1; seg < len; seg <<= 1)
		{
			step = seg << 1, tag = len / seg;
			for (now = 0; now < len; now += step)
			{
				bd = now + seg, id = 0;
				for (cur = now; cur < bd; ++cur, id += tag)
				{
					buf1 = dat[cur], buf2 = dat[cur + seg] * (Complex){w[id].re, typ * w[id].im};
					dat[cur] = buf1 + buf2, dat[cur + seg] = buf1 - buf2;
				}
			}
		}
		if (typ > 0) return;
		for (R int i = 0; i < len; ++i) dat[i].re /= len;
	}
	IN void Mul(R int up, int *m1, int *m2, int *res)
	{
		R int len = 1, lg = 0;
		Complex e, f, g, h;
		for (; len <= up; len <<= 1, lg++);
		for (R int i = 0; i < (len << 1); ++i) a[i] = b[i] = c[i] = d[i] = {0, 0};
		for (R int i = 1; i < len; ++i) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << lg - 1);
		for (R int i = 1; i < len; i <<= 1) for (R int j = 0; j < i; ++j)
		w[len / i * j] = {std::cos(j * PI / i), std::sin(j * PI / i)};
		for (R int i = 0; i < up; ++i)
		{
			m1[i] %= MOD, m2[i] %= MOD;
			a[i] = {m1[i] / BASE, 0};
			b[i] = {m1[i] % BASE, 0};
			c[i] = {m2[i] / BASE, 0};
			d[i] = {m2[i] % BASE, 0};
		}
		FFT(a, len, 1), FFT(b, len, 1), FFT(c, len, 1), FFT(d, len, 1);
		for (R int i = 0; i < len; ++i)
		{
			e = a[i], f = b[i], g = c[i], h = d[i];
			a[i] = e * g, b[i] = e * h, c[i] = f * g, d[i] = f * h;
		}
		FFT(a, len, -1), FFT(b, len, -1), FFT(c, len, -1), FFT(d, len, -1);
		for (R int i = 0; i < up; ++i)
		{
			res[i] = (ll)(a[i].re + 0.5) % MOD * BASE % MOD * BASE % MOD;
			(res[i] += (ll)(b[i].re + 0.5) % MOD * BASE % MOD) %= MOD;
			(res[i] += (ll)(c[i].re + 0.5) % MOD * BASE % MOD) %= MOD;
			(res[i] += (ll)(d[i].re + 0.5) % MOD) %= MOD;
		}
	}
	IN void Getinv(R int up)
	{
		if (up == 1) return ans[0] = fpow(ind[0], MOD - 2), void();
		int half = up + 1 >> 1; Getinv(half);
		for (R int i = 0; i < up; ++i) c1[i] = ind[i], c2[i] = ans[i];
		Mul(up, c1, c2, res);
		for (R int i = 0; i < up; ++i) c1[i] = res[i];
		Mul(up, c1, c2, res);
		for (R int i = 0; i < up; ++i) ans[i] = (2 * ans[i] % MOD - res[i] + MOD) % MOD;
	}
}
int main(void)
{
	in(n);
	for (R int i = 0; i < n; ++i) in(ind[i]);
	Poly::Getinv(n);
	for (R int i = 0; i < n; ++i) printf("%d ", ans[i]);
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
使用 JavaScript 编写的记忆游戏(附源代码)   项目:JavaScript 记忆游戏(附源代码) 记忆检查游戏是一个使用 HTML5、CSS 和 JavaScript 开发的简单项目。这个游戏是关于测试你的短期 记忆技能。玩这个游戏 时,一系列图像会出现在一个盒子形状的区域中 。玩家必须找到两个相同的图像并单击它们以使它们消失。 如何运行游戏? 记忆游戏项目仅包含 HTML、CSS 和 JavaScript。谈到此游戏的功能,用户必须单击两个相同的图像才能使它们消失。 点击卡片或按下键盘键,通过 2 乘 2 旋转来重建鸟儿对,并发现隐藏在下面的图像! 如果翻开的牌面相同(一对),您就赢了,并且该对牌将从游戏中消失! 否则,卡片会自动翻面朝下,您需要重新尝试! 该游戏包含大量的 javascript 以确保游戏正常运行。 如何运行该项目? 要运行此游戏,您不需要任何类型的本地服务器,但需要浏览器。我们建议您使用现代浏览器,如 Google Chrome 和 Mozilla Firefox, 以获得更好、更优化的游戏体验。要玩游戏,首先,通过单击 memorygame-index.html 文件在浏览器中打开游戏。 演示: 该项目为国外大神项目,可以作为毕业设计的项目,也可以作为大作业项目,不用担心代码重复,设计重复等,如果需要对项目进行修改,需要具备一定基础知识。 注意:如果装有360等杀毒软件,可能会出现误报的情况,源码本身并无病毒,使用源码时可以关闭360,或者添加信任。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值