多項式全家福(缺插值和點值)

寫法:

vector寫有什麼好處?

1.分治NTT的時候不用處理麻煩的下標。

2.傳進函數的時候不用傳指針而不會爆棧。

3.copy,swap,reverse,clear,resize等vector的函數可以幫助您快速地處理。

vector寫的時候注意什麼?

由於本人非常非常地懶,所以全部都是用vector寫的。

根據測試,在開了 O2優化的情況下,是沒有什麼區別的。

但是不開O2,你懂的……

還是想用vector怎麼辦呢?
又經過一輪測試,只要在dft的時候把vector裏的東西copy到一個數組,用這個數組做,再copy回去,是差不多的。


Dft:

不說了太累了。

我們設一次dft的常數爲1,以便後面分析複雜度。


求逆:

設對多項式A求逆。

倍增法。

設有兩個多項式BB0B、B0

B0A=1(mod xn/2)B0 * A=1(mod~x^{n/2})
BA=1(mod xn)B*A=1(mod~x^{n})

其實可以用B0B0去推BB,推導如下:

首先B0的初值即n=1時B0=a[0]1B0=a[0]^{-1}

B0A=1(mod xn/2)B0 * A=1(mod~x^{n/2})
B0A1=0(mod xn/2)B0 * A-1=0(mod~x^{n/2})
等式兩邊同時平方:
(B0A1)2=0(mod xn)(B0 * A-1)^2=0(mod~x^{n})
B02A22B0A+1=0(mod xn)B0^2*A^2-2B0*A+1=0(mod~x^{n})
2B0AB02A2=1(mod xn)2B0*A-B0^2*A^2=1(mod~x^{n})
按A整理:
A(2B0B02A)=1(mod xn)A*(2B0-B0^2*A)=1(mod~x^{n})
所以B=2B0B02A(mod xn)B=2B0-B0^2*A(mod~x^{n})

我們來思考一下怎麼做會快一點:
對B0求dft,對A求dft,然後算一算,再IDFT回去。
那麼需要三次,但是每層的次數界必須開到4*n

複雜度爲T(n)=nlogn+T(n/2)nlognT(n)=nlogn+T(n/2)≈nlogn
因爲n+n/2+n/4+<2nn+n/2+n/4+…<2n

所以複雜度大概爲6次dft


開二次根:

方法類似於求逆,也是用倍增的思想。

已知B02=A(mod xn/2)B0^2=A(mod~x^{n/2}),推B2=A(mod xn)B^2=A(mod~x^{n})

B0的初值可能需要二次剩餘,一般多項式開根的題的常數項是個常數,可以直接預處理。

二次剩餘戳這兒.

B02=B2(mod xn/2)B0^2=B^2(mod~x^{n/2})
B02B2=0(mod xn/2)B0^2-B^2=0(mod~x^{n/2})
(B02B2)2=0(mod xn)(B0^2-B^2)^2=0(mod~x^{n})
B042B02B2+B4=0(mod xn)B0^4-2B0^2*B^2+B^4=0(mod~x^{n})
B04+2B02B2+B4=4B02B2(mod xn)B0^4+2B0^2*B^2+B^4=4B0^2*B^2(mod~x^{n})
(B02+B2)2=(2B0B)2(mod xn)(B0^2+B^2)^2=(2B0B)^2(mod~x^{n})
B02+B2=2B0B(mod xn)B0^2+B^2=2B0B(mod~x^{n})
B02+A=2B0B(mod xn)B0^2+A=2B0B(mod~x^{n})
B=B0/2+A/(2B0)(mod xn)B=B0/2+A/(2B0)(mod~x^{n})

每層做的時候注意次數界是2n

每層要dftdft兩次和求逆一次,複雜度大概是2*(3+6)=18次dft?


取模:

給出A(x),B(x),deg(A)>=deg(B)A(x),B(x),deg(A)>=deg(B)
求滿足A(x)=B(x)C(x)+D(x)(deg(D)<deg(B))A(x)=B(x)*C(x)+D(x)(deg(D)<deg(B))CDC、D

n=deg(A),m=deg(B)n=deg(A),m=deg(B)
A(x)=B(x)C(x)+D(x)A(x)=B(x)*C(x)+D(x)
A(1x)=B(1x)C(1x)+D(1x)A({1\over x})=B({1\over x})*C({1\over x})+D({1\over x})
xnA(1x)=xmB(1x)xnmC(1x)+xnD(1x)x^n*A({1\over x})=x^m*B({1\over x})*x^{n-m}*C({1\over x})+x^n*D({1\over x})

我們來理一下次數的關係:
xnA(1x)[0..n]x^n*A({1\over x})∈[0..n]
xmB(1x)xnmC(1x)[0..n]x^m*B({1\over x})*x^{n-m}*C({1\over x})∈[0..n]
xnD(1x)[nm+1..n]x^n*D({1\over x})∈[n-m+1..n]

對等式兩邊同時mod xnm+1mod~x^{n-m+1},則xnD(1x)x^n*D({1\over x})沒了。

xnA(1x)=xmB(1x)xnmC(1x)(mod xnm+1)x^n*A({1\over x})=x^m*B({1\over x})*x^{n-m}*C({1\over x})(mod~x^{n-m+1})

ATA^T就是把A反過來。

那麼:
AT=BTCT(mod xnm+1)A^T=B^T*C^T(mod~x^{n-m+1})
C=(AT/BT mod xnm+1)TC=(A^T/{B^T}~mod~x^{n-m+1})^T

坑點:必須要先mod xnm+1mod~x^{n-m+1}再倒置。

求C要一次求逆和一次NTT,需要6+3=9次dft。

求了C後,D=ABCD=A-B*C,需要一次NTT,即3次dft。


對數:

前置技能:

1.複合函數求導:

F(G(x))=F(G(x))G(x)F(G(x))'=F'(G(x))*G'(x)

2.ln函數求導:

ln(x)=1xln(x)'={1\over x}


ln(A)ln(A)
=ln(x) dx=\int {ln(x)'} \,{\rm d}x
=ln(x)x dx=\int {ln'(x)*x'} \,{\rm d}x
=xx dx=\int {{x' \over x}} \,{\rm d}x

只需要求逆一次和NTT一次,9次dft


指數:

前置技能:

牛頓迭代:

https://blog.csdn.net/ccnt_2012/article/details/81837154

可以發現這就是個求函數零點的玩意兒,雖然有些侷限性,但是面對exp函數這麼優美的圖像肯定是沒有問題的。


現在來思考多項式牛頓迭代,即求f(B)=0f(B)=0,其中BB是一個多項式。

思路是倍增,即知道了前n/2n/2項的答案,去推前nn項的答案。

我們已知前n/2n/2項爲多項式B0,想要求出前nn項B。

可以直接由牛頓迭代得出:

B=B0f(B0)f(B0)(mod xn)B=B0-{f(B0)\over f'(B0)}(mod~x^{n})

下面介紹一下用泰勒展開推的方法:

f(B)=i=0f(i)(B0)(BB0)i/i!f(B)=\sum_{i=0}^{∞}f^{(i)}(B0)*(B-B0)^i/i!

注意當i>1時,每一項最低次項都是xnx^{n}

即:
f(B)=f(B0)+f(B0)(BB0)(mod xn)f(B)=f(B0)+f'(B0)*(B-B0)(mod~x^n)
f(B)=0(mod xn)∵f(B)=0(mod~x^n)
f(B0)+f(B0)(BB0)=0(mod xn)∴f(B0)+f'(B0)*(B-B0)=0(mod~x^n)
B=B0f(B0)f(B0)(mod xn)B=B0-{f(B0)\over f'(B0)}(mod~x^n)


會了牛頓迭代之後思考如何把exp(A)exp(A)換出牛頓迭代的形式:
exp(A)=Bexp(A)=B
等式兩邊同時取lnln,得A=ln(B)A=ln(B)
那麼f(B)=ln(B)Af(B)=ln(B)-A,現在就是求ff的零點。

套多項式牛頓迭代式得:
f(B)=B0f(B0)f(B0)(mod xn)f(B)=B0-{f(B0)\over f'(B0)}(mod~x^n)
f(B)=ln(B)Af(B)=ln(B)-A代入,得:
f(B)=B0(ln(B0)A)/(ln(B0)A)(mod xn)f(B)=B0-(ln(B0)-A)/(ln(B0)-A)'(mod~x^n)
f(B)=B0(ln(B0)A)/(ln(B0)A)(mod xn)f(B)=B0-(ln(B0)-A)/(ln(B0)'-A')(mod~x^n)
注意這裏的函數的自變量是一個多項式,因變量也是。
AA的話其實算作常數,求導之後是沒有的。
因爲自變量是多項式,所以ln(B0)ln(B0)'不算複合函數求導,只是對lnln求導,ln(B0)=1/B0ln(B0)'=1/B0
f(B)=B0(ln(B0)A)B0(mod xn)f(B)=B0-(ln(B0)-A)*B0(mod~x^n)
f(B)=B0(1ln(B0)+A)(mod xn)f(B)=B0*(1-ln(B0)+A)(mod~x^n)

每層需要用到lnln和一次乘法,複雜度爲(9+3)2=24(9+3)*2=24次dft

模板:

#include<ctime>
#include<vector>
#include<cstdio>
#include<algorithm>
#define ll long long
#define pp printf
#define fo(i, x, y) for(int i = x, B = y; i <= B; i ++)
#define ff(i, x, y) for(int i = x, B = y; i <  B; i ++)
#define fd(i, x, y) for(int i = x, B = y; i >= B; i --)
#define pb push_back
#define si size()
using namespace std;

typedef vector<ll> V;

const int N = (1 << 19) + 5;

const int mo = 998244353;

const int ni2 = (mo + 1) / 2;

ll ksm(ll x, ll y) {
    ll s = 1;
    for(; y; y /= 2, x = x * x % mo)
        if(y & 1) s = s * x % mo;
    return s;
}

int r[N]; ll aa[N];

void dft(V &b, int f) {
	int n = b.si;
    ff(i, 0, n) aa[i] = b[i];
    ff(i, 0, n) {
        r[i] = r[i / 2] / 2 + (i & 1) * (n / 2);
        if(i < r[i]) swap(aa[i], aa[r[i]]);
    }
    for(int h = 1; h < n; h *= 2) {
        ll wn = ksm(ksm(3, (mo - 1) / 2 / h), f == -1 ? mo - 2 : 1);
        for(int j = 0; j < n; j += 2 * h) {
            ll A, W = 1, *l = aa + j, *r = aa + j + h;
            ff(i, 0, h) {
                A = *r * W, *r = (*l - A) % mo, *l = (*l + A) % mo;
                W = W * wn % mo; l ++; r ++;
            }
        }
    }
    if(f == -1) {
        ll v = ksm(n, mo - 2);
        ff(i, 0, n) aa[i] = (aa[i] + mo) * v % mo;
    }
    ff(i, 0, n) b[i] = aa[i];
}

V operator * (V a, V b) {
    int z = a.si + b.si - 1;
    int n = 1; while(n < z) n *= 2;
    a.resize(n); b.resize(n);
    dft(a, 1); dft(b, 1);
    ff(i, 0, n) a[i] = a[i] * b[i] % mo;
    dft(a, -1); a.resize(z); return a;
}

V operator + (V a, V b) {
	int sz = max(a.si, b.si); a.resize(sz); b.resize(sz);
	ff(i, 0, sz) a[i] = (a[i] + b[i] + mo) % mo;
	return a;
}

V operator - (V a, V b) {
	int sz = max(a.si, b.si); a.resize(sz); b.resize(sz);
	ff(i, 0, sz) a[i] = (a[i] - b[i] + mo) % mo;
	return a;
}

V qni(V a) {
    int n0 = 1; while(n0 < a.si) n0 *= 2;
    V b; b.clear(); b.pb(ksm(a[0], mo - 2));
    for(int n = 2; n <= n0; n *= 2) {
        b.resize(n * 2); dft(b, 1);
        V c = a; c.resize(n); c.resize(n * 2); dft(c, 1);
        ff(i, 0, n * 2) b[i] = (b[i] * 2 - b[i] * b[i] % mo * c[i]) % mo;
        dft(b, -1); b.resize(n);
    }
    b.resize(a.si); return b;
}

void fan(V &a) {
	reverse(a.begin(), a.end());
}

V div(V a, V b) {
	int n = a.size() - b.size() + 1;
    fan(a); fan(b);
    b.resize(a.size()); b = qni(b);
    a = a * b; a.resize(n);
    fan(a);
    return a;
} 

V qmo(V a, V b) {
	return a - (b * (div(a, b)));
}

ll w;
struct P {
	ll x, y;
	P(){}
	P(ll _x, ll _y) {x = _x, y = _y;}
};
P operator *(P a, P b) { return P((a.x * b.x + a.y * b.y % mo * w) % mo, (a.x * b.y + a.y * b.x) % mo);}
int pd(ll x) {
	return ksm(x, (mo - 1) / 2) == 1;
}
P ksm(P x, ll y) {
	P s = P(1, 0);
	for(; y; y /= 2, x = x * x)
		if(y & 1) s = s * x;
	return s;
}
ll Sqrt(ll n) {
	if(!n) return 0;
	while(1) {
		ll a = rand() % mo;
		w = (a * a - n + mo) % mo;
		if(pd(w)) continue;
		return ksm(P(a, 1), (mo + 1) / 2).x;
	}
}

V Sqrt(V a) {
	int n0 = 1; while(n0 < a.si) n0 *= 2;
	V b; b.clear(); b.push_back(Sqrt(a[0])); b[0] = 1;
	for(int n = 2; n <= n0; n *= 2) {
		V c = a; c.resize(n);
		V d = b; d.resize(n);
		ff(i, 0, d.si) d[i] = d[i] * 2 % mo;
		ff(i, 0, b.si) b[i] = b[i] * ni2 % mo;
		c = c * qni(d);
		b = b + c;
	}
	b.resize(n0); return b;
}

V qd(V a) {
    a[0] = 0;
    ff(i, 1, a.si) a[i - 1] = a[i] * i % mo;
    return a;
}

V jf(V a) {
    a.pb(0);
    fd(i, a.si, 1) a[i] = a[i - 1] * ksm(i, mo - 2) % mo;
    a[0] = 0;
    return a;
}

V ln(V a) {
    int sa = a.si;
    V b = a; b = qni(b); a = qd(a);
    a = a * b; a = jf(a); a.resize(sa);
    return a;
}

V exp(V a) {
    int n0 = 1; while(n0 < a.si) n0 *= 2;
    V b; b.clear(); b.pb(1);
    for(int n = 2; n <= n0; n *= 2) {
		V c = b; c.resize(n); c = ln(c);
		V d = a; d.resize(n);
		c = c - d;
		c.resize(2 * n); dft(c, 1);
		b.resize(2 * n); dft(b, 1);
		ff(i, 0, 2 * n) b[i] = (b[i] - c[i] * b[i]) % mo;
		dft(b, -1); b.resize(n);
	}
	b.resize(a.si);
	return b;
}

int n, m;

V a, b;

int main() {
	srand(time (0));
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章