#563. 「LibreOJ Round #10」Snakes 的 Naïve Graph
Description
-
有一张二分图 G ( m ) G(m) G(m),其中有 ( m − 1 ) (m - 1) (m−1) 个黑色点与 ( m − 1 ) (m - 1) (m−1) 个白色点。黑点 i i i 与白点 j j j 有一条无向边,当且仅当下列条件至少有一条成立:
-
i = j i = j i=j
-
i ⋅ j ≡ 1 ( m o d m ) i\cdot j \equiv 1\pmod m i⋅j≡1(modm)
-
-
令 f [ G ( m ) ] f[G(m)] f[G(m)] 表示 G ( m ) G(m) G(m) 的本质不同的最大匹配的个数。
-
共 q q q 组询问,每组询问给出 l , r l, r l,r,求 ∑ i = l r f [ G ( i ) ] m o d 311021 \sum_{i = l}^r f[G(i)]\bmod 311021 ∑i=lrf[G(i)]mod311021。
-
1 ≤ l ≤ r ≤ 1 0 7 , 1 ≤ q ≤ 1 0 5 1\le l\le r\le 10^7, 1\le q\le 10^5 1≤l≤r≤107,1≤q≤105。
Solution
一道披着二分图外表的 Math \text{Math} Math。
校内 OJ 甚至把这题放入了二分图专题内(
i ⋅ j ≡ 1 ( m o d m ) i\cdot j \equiv 1\pmod m i⋅j≡1(modm) 说明 i i i 和 j j j 互为模 m m m 下的逆元,那么必须满足 i , j i, j i,j 都与 m m m 互质。
当 m ≥ 2 m\ge 2 m≥2 时, 1 ∼ m − 1 1\sim m - 1 1∼m−1 中与 m m m 互质的数有 φ ( m ) \varphi(m) φ(m) 个。
由于一个数的逆元唯一,故一个数不可能成为多个数的逆元,那么最大匹配要么就是黑 i i i 连白 i i i,要么就是黑 i i i 连白 i − 1 i^{-1} i−1。
- 如果黑 i i i 连白 i i i,那么黑 i − 1 i^{-1} i−1 就不能连白 i i i 了,只能连白 i − 1 i^{-1} i−1;
- 如果黑 i i i 连白 i − 1 i^{-1} i−1,那么同理可得黑 i − 1 i^{-1} i−1 连白 i i i。
所以每对逆元 2 2 2 种连边方法,记逆元对数为 n n n,则 f [ G ( m ) ] = 2 n f[G(m)] = 2^n f[G(m)]=2n。
但是当 i 2 ≡ 1 ( m o d m ) i^2 \equiv 1 \pmod m i2≡1(modm) 时被算了 2 2 2 次,需要减掉一次。
将
m
m
m 分解质因数
m
=
∏
i
=
1
k
p
i
α
i
m = \prod_{i = 1}^k p_i^{\alpha_i}
m=i=1∏kpiαi
根据中国剩余定理
x
2
≡
1
(
m
o
d
m
)
⟺
{
x
2
≡
1
(
m
o
d
p
1
α
1
)
x
2
≡
1
(
m
o
d
p
2
α
2
)
⋯
x
2
≡
1
(
m
o
d
p
k
α
k
)
x^2 \equiv 1 \pmod m \iff \begin{cases} x^2 \equiv 1\pmod {p_1^{\alpha_1}} \\ x^2 \equiv 1\pmod {p_2^{\alpha_2}} \\ \cdots \\ x^2 \equiv 1\pmod {p_k^{\alpha_k}} \end{cases}
x2≡1(modm)⟺⎩⎪⎪⎪⎨⎪⎪⎪⎧x2≡1(modp1α1)x2≡1(modp2α2)⋯x2≡1(modpkαk)
所以分别求出每一个同余方程的解数,然后相乘即可。
现在考虑
x
2
≡
1
(
m
o
d
p
α
)
x^2 \equiv 1\pmod {p^\alpha}
x2≡1(modpα)
根据平方差公式
(
x
−
1
)
(
x
+
1
)
≡
0
(
m
o
d
p
α
)
(x - 1)(x + 1) \equiv 0 \pmod {p^\alpha}
(x−1)(x+1)≡0(modpα)
-
当 p > 2 p > 2 p>2 时, ( x − 1 ) (x - 1) (x−1) 和 ( x + 1 ) (x + 1) (x+1) 不可能同时为 p p p 的倍数, x − 1 ≡ 0 ( m o d p α ) x - 1 \equiv 0\pmod {p^\alpha} x−1≡0(modpα) 或 x + 1 ≡ 0 ( m o d p α ) x + 1\equiv 0 \pmod {p^\alpha} x+1≡0(modpα), 2 2 2 种;
-
当 p = 2 p = 2 p=2 时, ( x − 1 ) (x - 1) (x−1) 和 ( x + 1 ) (x + 1) (x+1) 奇偶性相同,则必须均为 2 2 2 的倍数:
- α = 1 \alpha = 1 α=1: m o d 2 \bmod 2 mod2 余数均为 0 0 0,共 1 1 1 种;
- α = 2 \alpha = 2 α=2: m o d 4 \bmod 4 mod4 余数分别为为 0 , 2 0, 2 0,2 或 2 , 0 2, 0 2,0,共 2 2 2 种;
- α ≥ 3 \alpha \ge 3 α≥3:注意到不可能均为 4 4 4 的倍数,所以 m o d 2 α \bmod 2^\alpha mod2α 余数分别为 0 , 2 0, 2 0,2 或 2 α − 1 , 2 2^{\alpha - 1}, 2 2α−1,2 或 2 , 2 α − 1 2, 2^{\alpha - 1} 2,2α−1 或 2 , 0 2, 0 2,0,共 4 4 4 种;
于是用线性筛,分别记录 p h i n phi_n phin 表示 φ ( n ) \varphi(n) φ(n), t o t n tot_n totn 表示 n n n 除了 2 2 2 外含有多少个本质不同质因数, p f 2 n pf2_n pf2n 表示 n n n 含有多少个质因数 2 2 2。
再预处理出 p w 2 n pw2_n pw2n 表示 2 n m o d 311021 2^n\bmod 311021 2nmod311021。
那么就可以计算 d i f n dif_n difn 表示满足 x 2 ≡ 1 ( m o d n ) x^2\equiv 1\pmod n x2≡1(modn) 的 x x x 的个数,于是 f [ G ( m ) ] = 2 ( p h i m − d i f m 2 ) = p w 2 ( p h i m − d i f m 2 ) f[G(m)] = 2^{\left(\frac{phi_m - dif_m}{2}\right)} = pw2_{\left(\frac{phi_m - dif_m}{2} \right)} f[G(m)]=2(2phim−difm)=pw2(2phim−difm)。
有一个问题,此处的 p h i phi phi 和 d i f dif dif 都是不能取模的,但计算 d i f dif dif 时要用到 p w 2 pw2 pw2,而 p w 2 pw2 pw2 又必须取模。
其实当 n ≤ 1 0 7 n\le 10^7 n≤107 时 max { t o t n } ≤ 7 \max\{tot_n\} \le 7 max{totn}≤7, 2 7 2^7 27 是不会超过模数的,所以正常取模即可。
最后记录前缀和就做完了。
Code
// 18 = 9 + 9 = 18.
#include <iostream>
#include <cstdio>
#include <cstring>
#define Debug(x) cout << #x << "=" << x << endl
typedef long long ll;
using namespace std;
const int MAXN = 1e7 + 5;
const int N = 1e7;
typedef int arr[MAXN];
const int MOD = 311021;
int add(int a, int b) {return (a + b) % MOD;}
int sub(int a, int b) {return (a - b + MOD) % MOD;}
int mul(int a, int b) {return (ll)a * b % MOD;}
arr p, phi, pf2, tot, pw2, dif, f, sum;
bool vis[MAXN];
void pre()
{
phi[1] = 1;
for (int i = 2; i <= N; i++)
{
if (!vis[i])
{
p[++p[0]] = i;
phi[i] = i - 1;
if (i == 2)
{
pf2[i] = 1;
}
else
{
tot[i] = 1;
}
}
for (int j = 1; j <= p[0] && i * p[j] <= N; j++)
{
int k = i * p[j];
vis[k] = true;
if (i % p[j] == 0)
{
phi[k] = phi[i] * p[j];
if (p[j] == 2)
{
pf2[k] = pf2[i] + 1;
tot[k] = tot[i];
}
else
{
pf2[k] = pf2[i];
tot[k] = tot[i];
}
break;
}
phi[k] = phi[i] * phi[p[j]];
if (p[j] == 2)
{
pf2[k] = 1;
tot[k] = tot[i];
}
else
{
tot[k] = tot[i] + 1;
}
}
}
pw2[0] = 1;
for (int i = 1; i <= N; i++)
{
pw2[i] = mul(pw2[i - 1], 2);
}
for (int i = 1; i <= N; i++)
{
dif[i] = pw2[tot[i]];
if (pf2[i] == 2)
{
dif[i] <<= 1;
}
else if (pf2[i] >= 3)
{
dif[i] <<= 2;
}
f[i] = pw2[(phi[i] - dif[i]) >> 1];
sum[i] = add(sum[i - 1], f[i]);
}
}
int main()
{
pre();
int q;
scanf("%d", &q);
while (q--)
{
int l, r;
scanf("%d%d", &l, &r);
printf("%d\n", sub(sum[r], sum[l - 1]));
}
return 0;
}