@bzoj - 3328@ PYXFIB


@description@

给定序列 F:\(F_0 = 1, F_1 =1, F_n = F_{n-1} + F_{n-2} (n > 1)\)

给定 n, k, p,保证 p 为质数且 p 除以 k 的余数为 1。求:

\[\sum_{i=0}^{\lfloor \frac{n}{k} \rfloor}C_{n}^{ik}\times F_{ik} \]

原题传送门。

@solution@

我们记 \(a_i = C_{n}^{i}\times F_{i}\),再记 \(G(x) = \sum a_ix^i\)。题目所求即 \(\sum a_i\times[k|i]\)

根据斐波那契通项公式,有 \(F_{i} = \frac{1}{\sqrt{5}}((\frac{1 + \sqrt{5}}{2})^{i+1} - (\frac{1 - \sqrt{5}}{2})^{i+1})\)
为了避免二次剩余问题,我们不妨将一个数记作 \(a + b\sqrt{5}\)

考虑当 k = 1 时,其实就是二项式定理。
当 k = 2 时,可以根据中学老师所教,通过将 \((a + b)^n\)\((a - b)^n\) 相加构造偶数项之和。
当 k 更大的时候,我们需要更普遍的算法。

考虑单位根 \(w_{k}^{i}\)。根据我们 fft 学到的知识,有 \(\sum_{i=0}^{k-1}w_{k}^{i\times d} = [k|d]\times d\)(这个用等比数列结合单位根的性质就可以证了)。

因此有 \(\frac{1}{k}\sum_{i=0}^{k-1}G(w_{k}^{i}) = \sum a_i\times[k|i]\)。发现 \(G(w_{k}^{i})\) 还是个二项式定理。

原根具有单位根的性质。同时题目保证 p 除以 k 的余数为 1,所以用原根代替单位根即可。

@accepted code@

#include <cstdio>
#include <algorithm>
using namespace std;

typedef long long ll;

const int SQ = 32000;

ll N; int K, P;

inline int add(int x, int y) {return (x + y >= P ? x + y - P : x + y);}
inline int sub(int x, int y) {return (x - y < 0 ? x - y + P : x - y);}
inline int mul(int x, int y) {return 1LL * x * y % P;}

int pow_mod(int b, int p) {
	int ret = 1;
	for(int i=p;i;i>>=1,b=mul(b,b))
		if( i & 1 ) ret = mul(ret, b);
	return ret;
}

int a[SQ + 5], acnt;
bool check_g(int x) {
	for(int i=1;i<=acnt;i++)
		if( pow_mod(x, (P - 1) / a[i]) == 1 )
			return false;
	return true;
}
int get_g() {
	int t = P - 1; acnt = 0;
	for(int i=2;i<=SQ;i++) {
		if( t % i == 0 ) {
			a[++acnt] = i;
			while( t % i == 0 )
				t /= i;
		}
	}
	if( t != 1 ) a[++acnt] = t;
	
	for(int i=2;;i++)
		if( check_g(i) ) return i;
}

struct mint{
	int a, b; mint() {}
	mint(int _x) : a(_x), b(0) {}
	mint(int _a, int _b) : a(_a), b(_b) {}
	friend mint operator + (mint a, mint b) {
		return mint(add(a.a, b.a), add(a.b, b.b));
	}
	friend mint operator - (mint a, mint b) {
		return mint(sub(a.a, b.a), sub(a.b, b.b));
	}
	friend mint operator * (mint a, mint b) {
		int p = add(mul(a.a, b.a), mul(5, mul(a.b, b.b)));
		int q = add(mul(a.a, b.b), mul(a.b, b.a));
		return mint(p, q);
	}
	friend mint operator / (mint a, int b) {
		int k = pow_mod(b, P - 2);
		return a * k;
	}
};

mint pow(mint b,ll p) {
	mint ret = 1;
	for(ll i=p;i;i>>=1,b=b*b)
		if( i & 1 ) ret = ret*b;
	return ret;
}

int cal(int x) {
	mint A = mint(1, 1) / 2;
	int ret = (pow(A * x + 1, N) * A).b;
	A = mint(1, P - 1) / 2;
	ret = sub(ret, (pow(A * x + 1, N) * A).b);
	return ret;
}
void solve() {
	scanf("%lld%d%d", &N, &K, &P);
	int g = get_g(), w = pow_mod(g, (P - 1)/K), ans = 0;
	for(int i=0,p=1;i<K;i++,p=mul(p, w))
		ans = add(ans, cal(p));
	printf("%d\n", mul(ans, pow_mod(K, P - 2)));
}

int main() {
	int T; scanf("%d", &T);
	while( T-- ) solve();
}

@details@

这种地方能用通项公式的话没有必要用矩阵求幂了,想起来也比较复杂。

posted @ 2020-03-05 11:55  Tiw_Air_OAO  阅读(85)  评论(0编辑  收藏  举报