Problem
【UR #5】怎样跑得更快
时间限制: 1Sec
空间限制: 256MB
大力水手问禅师:“大师,我觉得我光有力气是不够的。比如我吃菠菜可以让力气更大,但是却没有提升跑步的速度。请问怎样才能跑得更快?我试过吃白菜,没有效果。”
禅师浅笑,答:“方法很简单,不过若想我教你,你先看看这道UOJRound的C题。”
令 p=998244353(7×17×223+1,一个质数)。
给你整数 n,c,d。现在有整数 x1,⋯,xn 和 b1,⋯,bn 满足 0≤x1,⋯,xn,b1,⋯,bn<p,且对于 1≤i≤n 满足:
j=1∑ngcd(i,j)c⋅lcm(i,j)d⋅xj≡bimodp
其中 v≡umodp 表示 v 和 u 除以 p 的余数相等。gcd(i,j) 表示 i 和 j 的最大公约数,lcm(i,j) 表示 i 和 j 的最小公倍数。
有 q 个询问,每次给出 b1,⋯,bn,请你解出 x1,⋯,xn 的值。
输入格式
第一行四个整数 n,c,d,q。保证 n,q≥1。
接下来 q 行,每行 n 个整数依次表示 b1,⋯,bn。保证 0≤b1,⋯,bn<p。
输出格式
共 q 行,每行对给出的 b1,⋯,bn,输出对应的 x1,⋯,xn。
如果有多组解输出任意一组即可。
如果无解那么这一行只用输出一个整数 −1。
样例输入输出
样例一
Input
Output
1 2
| 499122179 998244352 499122176 998244352 1 1
|
Explanation
对于第一个询问,要满足的等式为:
⎩⎪⎪⎨⎪⎪⎧x1+x2+x3≡1modpx1+2x2+x3≡0modpx1+x2+3x3≡0modp
样例二
见样例数据下载。
限制与约定
对于所有数据,n⋅q≤3×105,0≤c,d≤109。
测试点编号 |
n |
q |
其他 |
1 |
≤3 |
=1 |
c,d≤1000 |
2 |
≤100 |
=1 |
c=d |
3∼4 |
≤100 |
≤10 |
保证有唯一解 |
5 |
≤100 |
≤1000 |
c=1,d=0 |
6∼8 |
≤100 |
≤1000 |
保证有唯一解 |
9∼11 |
≤1000 |
=10 |
保证有唯一解 |
12 |
≤1000 |
≤10 |
c=d |
13∼14 |
≤1000 |
≤10 |
保证有唯一解 |
15∼16 |
≤105 |
≤3 |
c=1,d=0 |
17 |
≤105 |
≤3 |
c=d |
18∼20 |
≤105 |
≤3 |
无 |
后记
还没听完题,大力水手就嘶吼着:“太难了我不会我不会!”,飞快地跑掉了。
禅师看着大力水手消失的背影,叹了口气说:“你们这些人啊,每天就想做些大水题,一碰到难题,跑得不知道比谁都快。”
后来大力水手把UOJRound的C题题面贴在了汽车的后挡风玻璃上,人类从此掌握了光速旅行的正确方式。
下载
样例数据下载
标签:莫比乌斯反演
Solution
先膜一发vfk的题解。
首先将题目中的式子转换一下:
j=1∑ngcd(i,j)c⋅lcm(i,j)d⋅xj≡bimodpj=1∑ngcd(i,j)c−d⋅id⋅jd⋅xj≡bimodp
设f(x)=xc−d,g(x)=xd,那么原式化为
j=1∑nf(gcd(i,j))⋅g(i)⋅g(j)⋅xj≡bimodp
此时可以强行”说一句废话“来加入反演。存在数论函数fr(n)使得f(n)=∑d∣nfr(d),那么fr(n)=f(n)−∑d∣n且d=nfr(d),这样若知道f,即可枚举约数求出fr,即
1 2 3 4
| for (int i = 1; i <= n; i++) fr[i] = f[i]; for (int i = 1; i <= n; i++) for (int j = i+i; j <= n; j += i) fr[j] -= fr[i];
|
将f(n)=∑d∣nfr(d)带入原式即可得到
j=1∑nd∣i,j∑fr(d)⋅g(i)⋅g(j)⋅xj≡bimodpd∣i∑fr(d)j为d的倍数且j≤n∑g(j)⋅xj≡bi⋅g(i)−1modp
令z(d)=∑j为d的倍数且j≤ng(j)⋅xj,fz(d)=fr(d)⋅z(d),那么
d∣i∑fz(d)≡bi⋅g(i)−1modp
我们知道右边,要求左边,于是再次做反演,即fz(n)=bn⋅g(n)−1−∑d∣n且d=nfz(d)modp,即
1 2 3 4
| for (int i = 1; i <= n; i++) fz[i] = b[i]*Pow(g(i), P-2); for (int i = 1; i <= n; i++) for (int j = i+i; j <= n; j += i) fz[j] -= fz[i];
|
这样就得到了fz的值,即得到fr⋅z的值,于是z(n)≡fz(n)⋅fr(n)−1即可算出z的值。
然后再尝试用z推出x。由于z(d)=∑j为d的倍数且j≤ng(j)⋅xj,设hx(n)=g(n)⋅xn,那么∑j为d的倍数且j≤nhx(j)=z(d)。
仍然是反演的形式,再次反演得到hx(d)=z(d)−∑j=1n[j>d][d∣j]⋅hx(j),即
1 2 3 4
| for (int i = 1; i <= n; i++) hx[i] = z[d]; for (int i = n; i >= 1; i--) for (int j = i+i; j <= n; j += i) hx[i] -= hx[j];
|
知道了hx,即可由xi=hx(i)⋅g(i)−1求出x的值。
总结一下,分为三次反演:
- 预处理f,g的值(线性筛),反演求fr
- 读入b,反演求fz,乘fr的逆元即可得到z
- 通过z反演求hx,乘g的逆元即可得到x
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| #include <bits/stdc++.h> #define MAX_N 100000 #define P 998244353 using namespace std; typedef long long lnt; template <class T> inline void read(T &x) { x = 0; int c = getchar(), f = 1; for (; !isdigit(c); c = getchar()) if (c == 45) f = -1; for (; isdigit(c); c = getchar()) (x *= 10) += f*(c-'0'); } int n, c, d, q, b[MAX_N+5], x[MAX_N+5]; lnt g[MAX_N+5], fr[MAX_N+5], fz[MAX_N+5], hx[MAX_N+5]; int cnt, pri[MAX_N+5]; bool NotPri[MAX_N+5]; inline lnt Pow(lnt x, int k) { lnt ret = 1; bool flag = (k < 0); for (k = abs(k); k; k >>= 1, (x *= x) %= P) if (k&1) (ret *= x) %= P; return flag ? Pow(ret, P-2) : ret; } void init() { fr[1] = g[1] = NotPri[1] = 1; for (int i = 2; i <= n; i++) { if (!NotPri[i]) pri[cnt++] = i, fr[i] = Pow(i, c-d), g[i] = Pow(i, d); for (int j = 0; j < cnt; j++) { if (i*pri[j] > n) break; NotPri[i*pri[j]] = true; fr[i*pri[j]] = fr[i]*fr[pri[j]]%P; g[i*pri[j]] = g[i]*g[pri[j]]%P; if (i%pri[j] == 0) break; } } for (int i = 1; i <= n; i++) for (int j = i+i; j <= n; j += i) (fr[j] += (P-fr[i])) %= P; } int main() { read(n), read(c), read(d), read(q), init(); for (bool flag = false; q--; flag = false) { for (int i = 1; i <= n; i++) read(b[i]); for (int i = 1; i <= n; i++) fz[i] = 1LL*b[i]*Pow(g[i], P-2)%P; for (int i = 1; i <= n; i++) for (int j = i+i; j <= n; j += i) (fz[j] += (P-fz[i])) %= P; for (int i = 1; i <= n; i++) if (!fr[i] && fz[i]) flag = true; if (flag) {puts("-1"); continue;} for (int i = 1; i <= n; i++) hx[i] = fz[i]*Pow(fr[i], P-2)%P; for (int i = n; i >= 1; i--) for (int j = i+i; j <= n; j += i) (hx[i] += (P-hx[j])) %= P; for (int i = 1; i <= n; i++) x[i] = (int)(hx[i]*Pow(g[i], P-2)%P); for (int i = 1; i <= n; i++) printf("%d ", x[i]); puts(""); } return 0; }
|