LOJ #150. 挑战多项式/多项式全家桶

题目

这个题堪称 简易模板全家桶

关于各个函数的实现,大多数都是利用牛顿迭代公式( x = x 0 − f ( x ) f ′ ( x ) x=x_0-\dfrac {f(x)}{f'(x)} x=x0f(x)f(x))+倍增。

以下为多项式全家桶证明:

下面的 f , b , a f,b,a f,b,a分别为已知多项式,当前所求多项式和上一个状态(规模小一半)的多项式.(简记 y = a ( x ) y=a(x) y=a(x))

sqrt \text{sqrt} sqrt

b ( x ) 2 = f ( x )    − > b ( x ) 2 − f ( x ) = 0 b(x)^2=f(x) ~~->b(x)^2-f(x)=0 b(x)2=f(x)  >b(x)2f(x)=0
设 φ ( b ( x ) ) = b ( x ) 2 − f ( x ) 设\varphi(b(x))=b(x)^2-f(x) φ(b(x))=b(x)2f(x)
则 φ ′ ( b ( x ) ) = 2 ∗ b ( x ) 则\varphi'(b(x))=2*b(x) φ(b(x))=2b(x)
此 时 , 我 们 求 函 数 零 点 此时,我们求函数零点 ,
b ( x ) = a ( x ) − φ ( a ( x ) ) φ ′ ( a ( x ) ) = a ( x ) − a 2 ( x ) − f ( x ) 2 a ( x ) = a 2 ( x ) + f ( x ) 2 a ( x ) b(x)=a(x)-\dfrac{\varphi(a(x))}{\varphi'(a(x))}=a(x)-\dfrac{a^2(x)-f(x)}{2a(x)}=\dfrac{a^2(x)+f(x)}{2a(x)} b(x)=a(x)φ(a(x))φ(a(x))=a(x)2a(x)a2(x)f(x)=2a(x)a2(x)+f(x)

y 2 + f 2 y \dfrac {y^2+f}{2y} 2yy2+f

inv \text{inv} inv

φ ( b ( x ) ) = b ( x ) f ( x ) − 1 \varphi(b(x))=b(x)f(x)-1 φ(b(x))=b(x)f(x)1

φ ′ ( b ( x ) ) = f ( x ) \varphi'(b(x))=f(x) φ(b(x))=f(x)

b ( x ) = a ( x ) − a ( x ) f ( x ) − 1 f ( x ) b(x)=a(x)-\dfrac{a(x)f(x)-1}{f(x)} b(x)=a(x)f(x)a(x)f(x)1

b ( x ) = a ( x ) − b ( x ) ( a ( x ) f ( x ) − 1 ) b(x)=a(x)-b(x)(a(x)f(x)-1) b(x)=a(x)b(x)(a(x)f(x)1)

由于 a ( x ) f ( x ) − 1 ≡ 0 ( m o d    x n / 2 ) a(x)f(x)-1\equiv 0(\mod x^{n/2}) a(x)f(x)10(modxn/2).

所以 b ( x ) b(x) b(x)的高 n / 2 n/2 n/2位乘上 a ( x ) f ( x ) − 1 a(x)f(x)-1 a(x)f(x)1 m o d    x n \mod x^n modxn意义下为0.

所以 b ( x ) b(x) b(x) a ( x ) a(x) a(x)在当前等价.

b ( x ) = a ( x ) − a ( x ) ( a ( x ) f ( x ) − 1 ) = a ( x ) ( 2 − a ( x ) f ( x ) ) b(x)=a(x)-a(x)(a(x)f(x)-1)=a(x)(2-a(x)f(x)) b(x)=a(x)a(x)(a(x)f(x)1)=a(x)(2a(x)f(x))

y ( 2 − y f ) y(2-yf) y(2yf)

ln ⁡ \ln ln

ln ⁡ ′ x = 1 x \ln 'x=\dfrac 1 x lnx=x1

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

对两边求导: b ′ ( x ) = i n v ( f ( x ) ) f ′ ( x ) b'(x)=inv(f(x))f'(x) b(x)=inv(f(x))f(x).(链式反应)

积分 b ′ ( x ) b'(x) b(x)即可得到 b ( x ) b(x) b(x).

∫ f ′ f \int \dfrac {f'}f ff

exp ⁡ \exp exp

因为 exp ⁡ , ln ⁡ \exp,\ln exp,ln为逆运算,所以可得:

b ( x ) = exp ⁡ f ( x ) → ln ⁡ b ( x ) = f ( x ) b(x)=\exp f(x)\rightarrow \ln b(x)=f(x) b(x)=expf(x)lnb(x)=f(x)

φ ( b ( x ) ) = ln ⁡ b ( x ) − f ( x ) \varphi(b(x))=\ln b(x)-f(x) φ(b(x))=lnb(x)f(x)

φ ′ ( b ( x ) ) = 1 b ( x ) \varphi'(b(x))=\dfrac 1{b(x)} φ(b(x))=b(x)1

则可得到: b ( x ) = a ( x ) ( 1 − ln ⁡ a ( x ) + f ( x ) ) b(x)=a(x)(1-\ln a(x)+f(x)) b(x)=a(x)(1lna(x)+f(x)).

y ( 1 − ln ⁡ y + f ) y(1-\ln y+f) y(1lny+f)

pow \text{pow} pow

f ( x ) k = e l n ( f ( x ) ) ∗ k f(x)^k=e^{ln(f(x))*k} f(x)k=eln(f(x))k

f(x)/g(x),f(x)mod g(x) \text{f(x)/g(x),f(x)mod g(x)} f(x)/g(x),f(x)mod g(x)

多项式带余除法.

f ( x ) = q ( x ) g ( x ) + r ( x ) ( m o d    x n ) f(x)=q(x)g(x)+r(x)(\mod x^n) f(x)=q(x)g(x)+r(x)(modxn).

已知 f , g f,g f,g q , r q,r q,r. f f f n n n次多项式, g g g m m m次多项式, r r r的次数 < m <m <m.

可以发现 x n f ( x − 1 ) x^n f(x^{-1}) xnf(x1)的系数为 f f f系数的翻转,我们简记这个多项式为 F ( x ) F(x) F(x).

则有: F ( x ) = x n q ( x − 1 ) g ( x − 1 ) + x n r ( x − 1 ) = x n − m q ( x − 1 ) x m g ( x − 1 ) + x n − m + 1 x m − 1 r ( x − 1 ) = Q ( x ) G ( x ) + x n − m + 1 R ( x ) ( m o d    x n ) F(x)=x^n q(x^{-1})g(x^{-1})+x^nr(x^{-1})=x^{n-m} q(x^{-1}) x^m g(x^{-1})+x^{n-m+1}x^{m-1}r(x^{-1})= Q(x)G(x)+x^{n-m+1}R(x)(\mod x^n) F(x)=xnq(x1)g(x1)+xnr(x1)=xnmq(x1)xmg(x1)+xnm+1xm1r(x1)=Q(x)G(x)+xnm+1R(x)(modxn)

我们可以发现 F ( x ) = Q ( x ) G ( x ) ( m o d    x n − m + 1 ) F(x)=Q(x)G(x)(\mod x^{n-m+1}) F(x)=Q(x)G(x)(modxnm+1).

这样求出 Q Q Q后即可得到 q , r q,r q,r.

代码:

upd on 2021/2/3,大改码风和修改一些实现可能的问题.

#include <bits/stdc++.h>
#define gc getchar()
#define TP template<class o>
#define TPP template<typename t1, typename t2>
#define rep(i, a, b) for (int i = a; i <= (int)(b); i++)
#define REP(i, a, b) for (int i = b; i >= (int)(a); i--)
#define FOR(i, n) rep(i, 1, n) 
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N = 1 << 19 | 10, mod = 998244353;

TP void qr(o &x) {
	char c = gc; int f = 1;
	while(!isdigit(c)) { if(c == '-') f = -1; c = gc;}
	while( isdigit(c)) x = x * 10 + c - '0', c = gc;
	x *= f;
}
template<class o, class ... O> void qr(o& x, O& ... y) {qr(x); qr(y...);}
TP void qw(o x) {
	if(x < 0) putchar('-'), x = -x;
	if(x / 10) qw(x / 10);
	putchar(x % 10 + '0');
}
TP void pr1(o x) {qw(x); putchar(' ');}

TPP void ad(t1 &x, t2 y) { x += y; if(x >= mod) x -= mod;}
TPP void dl(t1 &x, t2 y) { x -= y; if(x < 0) x += mod;}

ll power(ll a, ll b = mod - 2, ll p = mod) {
	ll c = 1; while(b) {
		if(b & 1) 	c = c * a % p;
		b /= 2; 	a = a * a % p;
	}
	return c;
}

namespace Cipolla {
	ll p = mod, w;
	struct CP {
		ll x, y;
		CP(ll a = 1, ll b = 0) : x(a), y(b){};
		CP operator *(CP b) const { return CP((x * b.x + y * b.y % p * w) % p, (x * b.y + y * b.x) % p); }
	};
	CP power(CP a, ll b = (p + 1) / 2) {
		CP c; while(b) {
			if(b & 1) c = c * a;
			b /= 2; a = a * a;
		}
		return c;
	}
	ll solve(ll n) {
		if(n <= 1) return n;
		ll a; do {
			a = ((ll)rand() << 30 | rand()) % p;
			w = (a * a - n % p + p) % p;
		} while(::power(w, (p - 1) / 2, p) != p - 1);
		ll x = power(CP(a, 1)).x; return min(x, p - x);
	}
}

namespace poly {
	const int g = 3; const ll inv2 = (mod + 1) / 2;
	int R[N], w[N], Inv[N];
	int calc(int x) {
		if((x & -x) == x) return x;
		int n = 1; while(n < x) n *= 2;
		return n;
	}
	int pre(int m) {
		int n = calc(m);
		FOR(i, n - 1) R[i] = (R[i >> 1] >> 1) | (i & 1 ? n >> 1 : 0);
		return n;
	}
	void init(int m) {
		int n = calc(m) * 2;
		Inv[0] = Inv[1] = 1; rep(i, 2, n) Inv[i] = (ll)Inv[mod % i] * (mod - mod / i) % mod;
		for(int i = 1; i < n; i *= 2) {
			ll s = 1, d = power(g, (mod - 1) / (2 * i));
			rep(j, 0, i - 1) w[i + j] = s, s = s * d % mod;
		}
	}
	void DFT(int *f, int n) {
		static ull p[N]; rep(i, 0, n - 1) p[R[i]] = f[i];
		for(int i = 1, t; i < n; i *= 2) for(int j = 0; j < n; j += 2 * i)
			rep(k, 0, i - 1) t = p[j + k + i] * w[i + k] % mod, p[j + k + i] = p[j + k] + mod - t, p[j + k] += t;
		rep(i, 0, n - 1) f[i] = p[i] % mod;
	}
	void IDFT(int *f, int n) {
		reverse(f + 1, f + n); DFT(f, n); ll inv = Inv[n];
		rep(i, 0, n - 1) f[i] = f[i] * inv % mod;
	}
	const int sz = sizeof(int);
	void copy(int *a, int *b, int n) {memcpy(a, b, sz * n);}
	void clear(int *a, int n) { memset(a + n, 0, sz * n);}
	void clear(int *a, int x, int y) {if(x < y) memset(a + x, 0, sz * (y - x)); }
	
	void dao(int *a, int *b, int n) {
		FOR(i, n - 1) a[i - 1] = (ll)b[i] * i % mod;
		a[n - 1] = 0;
	}
	void ji(int *a, int *b, int n) {
		REP(i, 1, n - 1) a[i] = (ll)b[i - 1] * Inv[i] % mod;
		a[0] = 0;
	}
	void mult(int *a, int *b, int n, int m) {
		static int c[N]; int len = pre(n + m);
		copy(c, b, m); clear(c, m, len); DFT(a, len); DFT(c, len);
		rep(i, 0, len - 1) c[i] = (ll)a[i] * c[i] % mod;
		IDFT(c, len); copy(a, c, n + m);
	}
	void getinv(int *a, int *b, int n) {
		static int c[N]; clear(a, 0, 2 * n); clear(b, n); clear(c, 0, 2 * n); a[0] = power(b[0]);
		for(int p = 2; p <= n; p *= 2) {
			int len = pre(p * 2); copy(c, b, p); clear(c, p); DFT(a, len); DFT(c, len);
			rep(i, 0, len - 1) a[i] = (ll)a[i] * (mod + 2 - (ll)a[i] * c[i] % mod) % mod;
			IDFT(a, len); clear(a, p);
		}
	}
	void getsqrt(int *a, int *b, int n) {
		static int c[N];
		clear(a, 0, 2 * n); clear(b, n);
		clear(c, 0, 2 * n);
		a[0] = Cipolla::solve(b[0]);
		for(int p = 2; p <= n; p *= 2) {
			getinv(c, a, p); mult(c, b, p, p);
			rep(i, 0, p - 1) a[i] = (ll)(a[i] + c[i]) * inv2 % mod;
		}
	}
	void getln(int *a, int *b, int n) {
		static int c[N]; dao(a, b, n); getinv(c, b, n);
		mult(c, a, n, n); ji(a, c, n);
	}
	void getexp(int *a, int *b, int n) {
		static int c[N]; clear(c, 0, 2 * n); a[0] = 1;
		for(int p = 2; p <= n; p *= 2) {
			getln(c, a, p); dl(c[0], 1);
			rep(i, 0, p - 1)  dl(c[i] = -c[i] + b[i], 0);
			mult(c, a, p, p); copy(a, c, p);
		}
	}
}

int n, m, t, f[N], g[N], h[N];

int main() {
	qr(n); qr(m); t = poly::calc(++n); poly::init(t);
	rep(i, 0, n - 1) qr(f[i]);
	poly::getsqrt(g, f, t);
	poly::getinv (h, g, t);
	poly::ji(g, h, t);
	poly::getexp(h, g, t);
	FOR(i, t - 1) dl(f[i], h[i]);
	f[0] = 1; poly::getln(g, f, t); g[0]++;
	poly::getln(f, g, t); rep(i, 0, t - 1) f[i] = (ll)f[i] * m % mod;
	poly::getexp(g, f, t); poly::dao(f, g, t); n--;
	rep(i, 0, n - 1) pr1(f[i]);
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值