『题解』LibreOJ #563. Snakes 的 Naïve Graph

#563. 「LibreOJ Round #10」Snakes 的 Naïve Graph

Description

  • 有一张二分图 G ( m ) G(m) G(m),其中有 ( m − 1 ) (m - 1) (m1) 个黑色点与 ( m − 1 ) (m - 1) (m1) 个白色点。黑点 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 ij1(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 1lr107,1q105

Solution

一道披着二分图外表的 Math \text{Math} Math

校内 OJ 甚至把这题放入了二分图专题内(

i ⋅ j ≡ 1 ( m o d m ) i\cdot j \equiv 1\pmod m ij1(modm) 说明 i i i j j j 互为模 m m m 下的逆元,那么必须满足 i , j i, j i,j 都与 m m m​ 互质。

m ≥ 2 m\ge 2 m2 时, 1 ∼ m − 1 1\sim m - 1 1m1 中与 m m m 互质的数有 φ ( m ) \varphi(m) φ(m) 个。

由于一个数的逆元唯一,故一个数不可能成为多个数的逆元,那么最大匹配要么就是黑 i i i 连白 i i i,要么就是黑 i i i 连白 i − 1 i^{-1} i1

  • 如果黑 i i i 连白 i i i,那么黑 i − 1 i^{-1} i1 就不能连白 i i i 了,只能连白 i − 1 i^{-1} i1
  • 如果黑 i i i 连白 i − 1 i^{-1} i1,那么同理可得黑 i − 1 i^{-1} i1 连白 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 i21(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=1kpiα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} x21(modm)x21(modp1α1)x21(modp2α2)x21(modpkαk)
所以分别求出每一个同余方程的解数,然后相乘即可。

现在考虑
x 2 ≡ 1 ( m o d p α ) x^2 \equiv 1\pmod {p^\alpha} x21(modpα)
根据平方差公式
( x − 1 ) ( x + 1 ) ≡ 0 ( m o d p α ) (x - 1)(x + 1) \equiv 0 \pmod {p^\alpha} (x1)(x+1)0(modpα)

  • p > 2 p > 2 p>2 时, ( x − 1 ) (x - 1) (x1) ( x + 1 ) (x + 1) (x+1) 不可能同时为 p p p 的倍数, x − 1 ≡ 0 ( m o d p α ) x - 1 \equiv 0\pmod {p^\alpha} x10(modpα) x + 1 ≡ 0 ( m o d p α ) x + 1\equiv 0 \pmod {p^\alpha} x+10(modpα) 2 2 2 种;

  • p = 2 p = 2 p=2 时, ( x − 1 ) (x - 1) (x1) ( 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 x21(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(2phimdifm)=pw2(2phimdifm)

有一个问题,此处的 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 n107 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;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值