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

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

Description

  • 有一张二分图 \(G(m)\),其中有 \((m - 1)\) 个黑色点与 \((m - 1)\) 个白色点。黑点 \(i\) 与白点 \(j\) 有一条无向边,当且仅当下列条件至少有一条成立:

    • \(i = j\)

    • \(i\cdot j \equiv 1\pmod m\)

  • \(f[G(m)]\) 表示 \(G(m)\) 的本质不同的最大匹配的个数。

  • \(q\) 组询问,每组询问给出 \(l, r\),求 \(\sum_{i = l}^r f[G(i)]\bmod 311021\)

  • \(1\le l\le r\le 10^7, 1\le q\le 10^5\)

Solution

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

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

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

\(m\ge 2\) 时,\(1\sim m - 1\) 中与 \(m\) 互质的数有 \(\varphi(m)\) 个。

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

  • 如果黑 \(i\) 连白 \(i\),那么黑 \(i^{-1}\) 就不能连白 \(i\) 了,只能连白 \(i^{-1}\)
  • 如果黑 \(i\) 连白 \(i^{-1}\),那么同理可得黑 \(i^{-1}\) 连白 \(i\)

所以每对逆元 \(2\) 种连边方法,记逆元对数为 \(n\),则 \(f[G(m)] = 2^n\)

但是当 \(i^2 \equiv 1 \pmod m\) 时被算了 \(2\) 次,需要减掉一次。

\(m\) 分解质因数

\[m = \prod_{i = 1}^k p_i^{\alpha_i} \]

根据中国剩余定理

\[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} \]

所以分别求出每一个同余方程的解数,然后相乘即可。

现在考虑

\[x^2 \equiv 1\pmod {p^\alpha} \]

根据平方差公式

\[(x - 1)(x + 1) \equiv 0 \pmod {p^\alpha} \]

  • \(p > 2\) 时,\((x - 1)\)\((x + 1)\) 不可能同时为 \(p\) 的倍数,\(x - 1 \equiv 0\pmod {p^\alpha}\)\(x + 1\equiv 0 \pmod {p^\alpha}\)\(2\) 种;

  • \(p = 2\) 时,\((x - 1)\)\((x + 1)\) 奇偶性相同,则必须均为 \(2\) 的倍数:

    • \(\alpha = 1\)\(\bmod 2\) 余数均为 \(0\),共 \(1\) 种;
    • \(\alpha = 2\)\(\bmod 4\) 余数分别为为 \(0, 2\)\(2, 0\),共 \(2\) 种;
    • \(\alpha \ge 3\):注意到不可能均为 \(4\) 的倍数,所以 \(\bmod 2^\alpha\) 余数分别为 \(0, 2\)\(2^{\alpha - 1}, 2\)\(2, 2^{\alpha - 1}\)\(2, 0\),共 \(4\) 种;

于是用线性筛,分别记录 \(phi_n\) 表示 \(\varphi(n)\)\(tot_n\) 表示 \(n\) 除了 \(2\) 外含有多少个本质不同质因数,\(pf2_n\) 表示 \(n\) 含有多少个质因数 \(2\)

再预处理出 \(pw2_n\) 表示 \(2^n\bmod 311021\)

那么就可以计算 \(dif_n\) 表示满足 \(x^2\equiv 1\pmod n\)\(x\) 的个数,于是 \(f[G(m)] = 2^{\left(\frac{phi_m - dif_m}{2}\right)} = pw2_{\left(\frac{phi_m - dif_m}{2} \right)}\)

有一个问题,此处的 \(phi\)\(dif\) 都是不能取模的,但计算 \(dif\) 时要用到 \(pw2\),而 \(pw2\) 又必须取模。

其实当 \(n\le 10^7\)\(\max\{tot_n\} \le 7\)\(2^7\) 是不会超过模数的,所以正常取模即可。

最后记录前缀和就做完了。

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;
}
posted @ 2022-02-09 21:06  mango09  阅读(26)  评论(0编辑  收藏  举报
-->