loj2000「SDOI2017」數字表格

在不開題解的情況下,成功騙了30分,以下是騙分的代碼

#include <cstdio>
#include <cstring>
#include <iostream>
#include <cmath>
using namespace std;
typedef long long LL;
const int N = 1e3 + 6, P = 1e9 + 7;
LL g[N][N], f[N];

LL ksm(LL a, LL b) {
	LL ans = 1LL;
	for (; b; b >>= 1) {
		if (b & 1) ans = ans * a % P;
		a = a * a % P;
	}
	return ans;
}

int gcd(int a, int b) {
	return !a ? b : gcd(b % a, a);
}

void Prepare() {
	f[0] = 0, f[1] = 1;
	for (int i = 2; i <= 1000; i++) 
		f[i] = (f[i-1] + f[i-2]) % P;
	for (int i = 1; i <= 1000; i++)
		g[1][i] = g[i][1] = 1;
	for (int i = 2; i <= 1000; i++)
		for (int j = 2; j <= 1000; j++) 
			g[i][j] = g[i][j-1] * g[i-1][j] % P * f[gcd(i,j)] % P * ksm(g[i-1][j-1], P - 2) % P;
}

int main() {
	Prepare();
	int T, x, y; scanf("%d", &T);
	while (T--) {
		scanf("%d%d", &x, &y);
		printf("%lld\n", g[x][y]);
	}
	return 0;
}

想不到正解主要是因爲我對莫比烏斯反演掌握的不好。
翻譯以下題意,題目讓我們求的是i=1Nj=1NFgcd(i,j)mod  P\prod_{i=1}^N \prod_{j=1}^NF_{gcd(i,j)}\mod P,其中F是斐波那契數列,P=109+7P=10^9+7,共有T組數據。
題解:
我們先使得n<=m
=i=1Nj=1NFgcd(i,j)mod  P=k=1NFk(i=1Nj=1M[gcd(i,j)=k]) =\prod_{i=1}^N \prod_{j=1}^NF_{gcd(i,j)}\mod P\\ =\prod_{k=1}^NF_k^{(\sum_{i=1}^N\sum_{j=1}^M[gcd(i,j)=k])}
然後我們把右上角這一大坨東西提取出來
=i=1Nj=1M[gcd(i,j)=k]=i=1Nkj=1Mkgcd(i,j)=1]=d=1Nkμ(d)NkdMkd =\sum_{i=1}^N\sum_{j=1}^M[gcd(i,j)=k]\\ =\sum_{i=1}^{ \left \lfloor \frac{N}{k} \right \rfloor}\sum_{j=1}^{\left \lfloor \frac{M}{k} \right \rfloor}gcd(i,j)=1]\\ =\sum_{d=1}^{\left \lfloor \frac{N}{k} \right \rfloor}μ(d)\left \lfloor \frac{N}{kd} \right \rfloor\left \lfloor \frac{M}{kd} \right \rfloor
所以
=k=1NFk(d=1Nkμ(d)NkdMkd) =\prod_{k=1}^NF_k^{(\sum_{d=1}^{\left \lfloor \frac{N}{k} \right \rfloor}μ(d)\left \lfloor \frac{N}{kd} \right \rfloor\left \lfloor \frac{M}{kd} \right \rfloor)}
越是我們設T=kd,然後將T提取出來,得到
=T=1N(kTFkμ(Tk))MTMT =\prod_{T=1}^N(\prod_{k|T}F_k^{μ(\frac{T}{k})})^{\left \lfloor \frac{M}{T} \right \rfloor\left \lfloor \frac{M}{T} \right \rfloor}
f(n)=dnFdμ(ndf(n)=\prod_{d|n}F_d^{μ(\frac{n}{d})}

=T=1Nf(T)MTMT =\prod_{T=1}^Nf(T)^{\left \lfloor \frac{M}{T} \right \rfloor\left \lfloor \frac{M}{T} \right \rfloor}
然後發現外層可以用數論分塊來做,也就是從i到n/(n/i)的n/i之都是相等的
有了這個,我們就要求ff數組的前綴的乘積。
那麼ff數組怎麼求呢,設wi=Fdμ(ndw_i=F_d^{μ(\frac{n}{d})}求出每一個i,然後對於每一個i的倍數,有f(ki)=wif(ki)*=w_i即可
參考代碼

#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long LL;
const LL N = 1e6 + 6, M = 8e4 + 6, P = 1e9 + 7;
int ksm(int a, LL b) { //快速冪 
	if (b < 0) b += P - 1; //因爲莫比烏斯函數有負數,所以我們要對負數進行特殊處理 
	int ans = 1;
	for (; b; a = (LL)a * a % P, b >>= 1)
		if (b & 1) ans = (LL)ans * a % P;
	return ans;
}

bool v[N];
int pr, prime[M], miu[N], f[N], inv[N];
//pr和prime表示質數,miu即爲μ,f表示f數組的前綴乘積,inv表示f的乘法逆元 
void init() {
	v[1] = 1, miu[1] = 1;
	for (int i = 2; i <= 1000000; i++) {//線性篩求μ函數 
		if (!v[i]) prime[++pr] = i, miu[i] = -1;
		for (int j = 1, k; j <= pr && (LL)i * prime[j] <= 1000000; j++) {
			k = prime[j] * i;
			v[k] = 1;
			if (i % prime[j]) miu[k] = -miu[i];
			else break;
		}
	}
	for (int i = 1; i <= 1000000; i++)
		f[i] = 1, inv[i] = 1;
	int l = 1, r = 0;//l和r表示斐波那契數列 
	for (int i = 1; i <= 1000000; i++) {
		r = (l + r) % P;
		l = (r - l + P) % P;
		int c[3] = {ksm(r, -1), 1, r};//求f數組 
		for (int j = i, k = 1; j <= 1000000; j += i, k++) {
			f[j] = (LL)f[j] * c[miu[k] + 1] % P;
			inv[j] = (LL)inv[j] * c[1 - miu[k]] % P;
		}
	}
	f[0] = inv[0] = 1;//求f數組的前綴乘積 
	for (int i = 1; i <= 1000000; i++) {
		f[i] = (LL)f[i - 1] * f[i] % P;
		inv[i] = (LL)inv[i-1] * inv[i] % P;
	}
}

int main() {
	init();
	int T; scanf("%d", &T);
	while (T--) {
		int n, m;
		scanf("%d%d", &n, &m);
		if (n > m) swap(n, m);
		int ans = 1;
		for (int i = 1, j; i <= n; i = j + 1) {
			j = min(n / (n / i), m / (m / i));//數論分塊 
			ans = (LL)ans * ksm((LL)f[j] * inv[i-1] % P, (LL)(n / i) * (m / i)) % P;
		}
		printf("%d\n", ans);
	}
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章