天天看點

【BZOJ3656】異或【擴充Lucas】【線性無關】

因為新數列中的數線性無關,即我們随意組合不會有重複結果,是以答案就是C(n, k) % p,因為p比較小,寫個擴充Lucas就好了。

證明一下線性無關:

首先有兩個引理:

定理1:一個n×n的矩陣A是非奇異的充要條件為 A的行列式不等于0。

定理2:令x1, x2, ..., xn為R^n中的n個向量,并令X = (x1, ...,xn)。向量x1,x2, ...,xn線性無關的充要條件是X為非奇異的。

是以我們隻需要計算這個行列式的值,如果結果不為0,那麼就是線性無關的。

我們把每個數都寫成二進制的形式,把二進制的每一位都看成一維。

比如6 = 110(2),那麼這個向量就是(1,1,0)。

舉個例子,我們證明1,2,4是線性無關的,首先我們寫出向量,分别為

(0,0,1),(0,1,0),(1,0,0)

得到行列式:

【BZOJ3656】異或【擴充Lucas】【線性無關】

(注意這裡是二進制的行列式。把加減法看做異或,把乘法看做與。)

上式不等于0,說明是線性無關的。

我們現在要證明新的數列1,3,5,10,17,39...是線性無關的,那麼我們把新的數清單示為行列式。

表示成的行列式一定為這種形式:

【BZOJ3656】異或【擴充Lucas】【線性無關】

(注意這裡是二進制的行列式。把加減法看做異或,把乘法看做與。)

它的行列式等于1,說明新數列線性無關。

Q:為什麼反對角線上都是1?

A:考慮原數列的行列式,反對角線上都是1。每個數與它的約數位置異或,這個約數位置的數的二進制長度比這個數要短,無論怎麼異或都不會改變這個1。

Q:為什麼這個行列式等于-1?

A:因為隻有反對角線上沒有0。

Q:為什麼有x。。

A:因為異或後的結果我也不知道是0是1呀...

Q.E.D.

直接上模闆即可。

#include <cstdio>

typedef unsigned long long ULL;
typedef long long LL;

inline void exgcd(ULL a, ULL b, LL &x, LL &y) {
	b ? (exgcd(b, a % b, y, x), y -= a / b * x) : (x = 1, y = 0);
}

inline ULL qpow(ULL x, ULL n, ULL p) {
	ULL ans = 1;
	for(ULL t = x; n; n >>= 1, t = x * x % p) if(n & 1) ans = ans * t % p;
	return ans;
}

inline ULL c1(ULL n, ULL p, ULL pk) {
	if(n == 0) return 1;
	ULL ans = 1;
	for(ULL i = 2; i <= pk; i++) if(i % p) ans = ans * i % pk;
	ans = qpow(ans, n / pk, pk);
	for(ULL k = n % pk, i = 2; i <= k; i++) if(i % p) ans = ans * i % pk;
	return ans * c1(n / p, p, pk) % pk;
}

inline ULL inv(ULL a, ULL m) {
	LL x, y;
	exgcd(a, m, x, y);
	if(x < 0) x += m;
	return x;
}

inline ULL calc(ULL n, ULL m, ULL p, ULL pi, ULL pk) {
	ULL a = c1(n, pi, pk), b = c1(m, pi, pk), c = c1(n - m, pi, pk), k = 0;
	for(ULL i = n; i; i /= pi) k += i / pi;
	for(ULL i = m; i; i /= pi) k -= i / pi;
	for(ULL i = n - m; i; i /= pi) k -= i / pi;
	ULL ans = a * inv(b, pk) % pk * inv(c, pk) % pk * qpow(pi, k, pk) % pk;
	return ans * (p / pk) % p * inv(p / pk, pk) % p;
}

int main() {
	ULL n, m, p;
	scanf("%llu%llu%llu", &n, &m, &p);

	ULL ans = 0;
	for(ULL i = 2, x = p; x > 1; i++) if(x % i == 0) {
		ULL k;
		for(k = 1; x % i == 0; x /= i) k *= i;
		ans = (ans + calc(n, m, p, i, k)) % p;
	}

	printf("%llu\n", ans);

	return 0;
}