【UR #5】怎样跑得更快 莫比乌斯反演

本文介绍了一种利用莫比乌斯反演和快速幂模运算解决特定形式线性方程组的方法,通过预处理得到莫比乌斯函数和逆元,将原方程转换并求解未知数序列。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description
给你一个b序列,求x序列,b和x满足下式。
∑j=1ngcd(i,j)c⋅lcm(i,j)d⋅xj≡bi(modp)\sum_{j = 1}^{n} gcd(i, j)^c \cdot lcm(i, j)^d \cdot x_j \equiv b_i \pmod{p}j=1ngcd(i,j)clcm(i,j)dxjbi(modp)


Sample Input
3 1 0 2
1 0 0
1 2 3


Sample Output
499122179 998244352 499122176
998244352 1 1


式子首先可以化成:
∑j=1njdgcd(i,j)c−d⋅xj≡biid(modp)\sum_{j = 1}^{n} j^dgcd(i, j)^{c-d} \cdot x_j \equiv \frac {b_i}{i^d} \pmod{p}j=1njdgcd(i,j)cdxjidbi(modp)
我们考虑设gig_igiidi^didfif_ifiic−di^{c-d}icdwiw_iwibiid\frac {b_i}{i^d}idbi
∑j=1ngj⋅fgcd(i,j)⋅xj≡wi(modp)\sum_{j = 1}^{n} g_j \cdot f_{gcd(i, j)} \cdot x_j \equiv w_i \pmod{p}j=1ngjfgcd(i,j)xjwi(modp)
考虑莫反搞掉这个gcdgcdgcd,设fri=∑d∣ifd⋅μidfr_i=\sum_{d|i}f_d \cdot \mu_{\frac id}fri=difdμdi
ti=xi⋅git_i=x_i \cdot g_iti=xigi,那么式子变为:
∑j=1n∑d∣i,d∣jfrd⋅tj≡wi(modp)\sum_{j = 1}^{n}\sum_{d|i,d|j} fr_d \cdot t_j \equiv w_i \pmod{p}j=1ndi,djfrdtjwi(modp)
∑d∣ifrd∑d∣jtj≡wi(modp)\sum_{d|i} fr_d \sum_{d|j} t_j \equiv w_i \pmod{p}difrddjtjwi(modp)
qd=∑d∣jtjq_d=\sum_{d|j} t_jqd=djtj
∑d∣ifrd⋅qd≡wi(modp)\sum_{d|i} fr_d\cdot q_d \equiv w_i \pmod{p}difrdqdwi(modp)
Fd=frd⋅qdF_d=fr_d\cdot q_dFd=frdqd
莫反一下,这样就搞得出FFF,进而搞得出qqq
对于这个qqq再莫反一下,就搞了出ttt


#include <cstdio>
#include <cstring>

using namespace std;
typedef long long LL;
const int N = 300001;
const LL mod = 998244353;
int read() {
	int s = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
	return s * f;
}
void put(int x) {
	if(x >= 10) put(x / 10);
	putchar(x % 10 + '0');
}

LL w[N], g[N], f[N], fr[N], F[N], q[N], t[N], x[N];
int plen, p[N], mu[N];
bool v[N];

void get_p() {
	mu[1] = 1;
	for(int i = 2; i < N; i++) {
		if(!v[i]) p[++plen] = i, mu[i] = -1;
		for(int j = 1; j <= plen && (LL)p[j] * i < N; j++) {
			v[i * p[j]] = 1;
			if(i % p[j] == 0) {mu[i * p[j]] = 0; break;}
			mu[i * p[j]] = -mu[i];
		}
	}
}

LL pow_mod(LL a, int k) {
	LL ans = 1;
	k = (k + mod - 1) % (mod - 1);
	while(k) {
		if(k & 1) (ans *= a) %= mod;
		(a *= a) %= mod; k /= 2;
	} return ans;
}

int main() {
	int n = read(), c = read(), d = read(), tt = read();
	get_p();
	for(int i = 1; i <= n; i++) g[i] = pow_mod(i, d), f[i] = pow_mod(i, c - d);
	for(int i = 1; i <= n; i++) {
		for(int j = i, s = 1; j <= n; j += i, s++) {
			(fr[j] += mu[s] * f[i]) %= mod;
		} (fr[i] += mod) %= mod;
	}
	while(tt--) {
		bool bk = 0;
		memset(F, 0, sizeof(F));
		memset(t, 0, sizeof(t));
		for(int i = 1; i <= n; i++) w[i] = read(), w[i] = w[i] * pow_mod(g[i], mod - 2) % mod;
		for(int i = 1; i <= n; i++) {
			for(int j = i, s = 1; j <= n; j += i, s++) {
				(F[j] += mu[s] * w[i]) %= mod;
			} (F[i] += mod) %= mod;
		} for(int i = 1; i <= n; i++) {
			if(F[i] != 0 && fr[i] == 0) {bk = 1; break;}
			q[i] = F[i] * pow_mod(fr[i], mod - 2) % mod;
		} if(bk) {puts("-1"); continue;}
		for(int i = 1; i <= n; i++) {
			for(int j = i, s = 1; j <= n; j += i, s++) {
				(t[i] += mu[s] * q[j]) %= mod;
			} (t[i] += mod) %= mod;
		} for(int i = 1; i <= n; i++) {
			if(t[i] != 0 && g[i] == 0) {bk = 1; break;}
			x[i] = t[i] * pow_mod(g[i], mod - 2) % mod;
		} if(bk) {puts("-1"); continue;}
		for(int i = 1; i <= n; i++) put(x[i]), putchar(' ');
		putchar('\n');
	}
	return 0;
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值