loj Snakes 的 Naïve Graph 【数论】

题目链接

loj

题解

感谢珂神的指导orz

观察式子\(i \times j \equiv 1 \pmod m\),显然\(i,j\)是模\(m\)意义下成对的逆元,只需统计模\(m\)意义下存在逆元的数的个数,即与\(m\)互质的数的个数\(\varphi(m)\)
每对逆元的连边有两种情况,记逆元对数为\(x\),则方案数为\(2^x\)

真的完了吗?难点才刚开始
\(m\)意义下有的数逆元为本身!此时不能计入答案
所以我们还需求模\(m\)意义下逆元为本身的数的个数

重新理解一下中国剩余定理,本质是环同构
\(m = a_1a_2a_3\dots\)
其中\(a_i = p_i^{k_i}\)
那么首先在模\(a_i\)意义下分别有一个整环
我们把这些整环放在一起,用向量表示,成一个新环,可与模\(m\)意义下的整环一一对应,即环同构
例如\(Z/6 = Z/2 \centerdot Z/3\)
则对应关系
0 (0,0)
1 (1,1)
2 (0,2)
3 (1,0)
4 (0,1)
5 (1,2)

环同样满足加减乘
所以我们需要求出在模每个\(a_i\)下平方等于\(1\)的数的个数,乘起来即可

\[\begin{aligned} x^2 \equiv 1 \pmod m \\ (x + 1)(x - 1) \equiv 0 \pmod m \end{aligned} \]

所以我们只需\((x + 1)(x - 1)\)\(m\)的倍数即可
对于\(p > 2\)\(p\)不能同时整除\((x + 1)\)\((x - 1)\),所以只能一者为\(0\),所以有两种方案
对于\(p = 2\)\((x + 1)(x - 1)\)必须都是偶数,显然有一个不能整除\(4\),所以另一个应能整除\(2^{k-1}\),或者两个如\(p > 2\)的情况,有一者为\(0\),但是\(k \le 2\)时会重复,所以特判一下,\(k = 1\)\(1\)\(k = 2\)\(2\),否则为\(4\)
以上线筛处理即可

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<map>
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define mp(a,b) make_pair<int,int>(a,b)
#define cls(s) memset(s,0,sizeof(s))
#define cp pair<int,int>
#define LL long long int
#define res register
using namespace std;
const int maxn = 10000005,maxm = 100005,INF = 1000000000,P = 311021;
inline int read(){
	int out = 0,flag = 1; char c = getchar();
	while (c < 48 || c > 57){if (c == '-') flag = -1; c = getchar();}
	while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
	return out * flag;
}
int p[maxn],pi,isn[maxn],phi[maxn],N = 10000000;
int f[maxn],bin[maxn],tw[maxn],tot[maxn],all[maxn];
void init(){
	phi[1] = 1;
	for (res int i = 2; i <= N; i++){
		if (!isn[i]){
			p[++pi] = i,phi[i] = i - 1;
			if (i == 2) tw[i] = 1;
			else tot[i] = 1;
		}
		for (res int j = 1; j <= pi && i * p[j] <= N; j++){
			isn[i * p[j]] = true;
			if (i % p[j] == 0){
				phi[i * p[j]] = phi[i] * p[j];
				if (p[j] == 2){
					tw[i * p[j]] = tw[i] + 1;
					tot[i * p[j]] = tot[i];
				}
				else{
					tw[i * p[j]] = tw[i];
					tot[i * p[j]] = tot[i];
				}
				break;
			}
			phi[i * p[j]] = phi[i] * (p[j] - 1);
			if (p[j] == 2){
				tot[i * p[j]] = tot[i];
				tw[i * p[j]] = tw[i] + 1;
			}
			else{
				tot[i * p[j]] = tot[i] + 1;
				tw[i * p[j]] = tw[i];
			}
		}
	}
	bin[0] = 1;
	for (res int i = 1; i <= N; i++)
		bin[i] = (bin[i - 1] << 1),bin[i] >= P ? (bin[i] -= P) : 0;
	for (res int i = 1; i <= N; i++){
		all[i] = bin[tot[i]];
		if (tw[i] == 2) all[i] = all[i] * 2 % P;
		else if (tw[i] > 2) all[i] = all[i] * 4 % P;
	}
	for (res int i = 1; i <= N; i++){
		f[i] = f[i - 1] + bin[(phi[i] - all[i]) >> 1],f[i] >= P ? (f[i] -= P) : 0;
	}
}
int c[maxn],ci;
int main(){
	init();
	int q = read(),l,r;
	while (q--){
		l = read(); r = read();
		printf("%d\n",(f[r] - f[l - 1] + P) % P);
	}
	return 0;
}

posted @ 2018-06-25 07:37  Mychael  阅读(168)  评论(0编辑  收藏  举报