8.7 数论专题

exgcd

P5656 【模板】二元一次不定方程 (exgcd)

可以先用exgcd求出一组特解

然后再搞出最小的正整数xx来推最大的y 再用最小的正整数y来推最大的x即可

最后通过(xmaxxmin)/b+1来获取解的个数 如果没有正整数解就输出xminymin(因为已经保证这两个东西都大于0了)

#include <bits/stdc++.h>
using namespace std;
#define mid (l+r>>1)
#define endl '\n'
#define inl inline
#define int long long 
const int inf = 0x3f3f3f3f;
const int N = 5e5 + 5;
char buf[1<<24] , *p1 , *p2;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<24,stdin),p1==p2)?EOF:*p1++)
//#define getchar() cin.get();
int read()
{
	int x = 0 , f = 1;
	char ch = getchar();
	while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
	while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
	return x * f ;
}

int n , m;

int gcd ( int a , int b )
{
	if ( a % b == 0 ) return b;
	return gcd ( b , a % b );
}

void exgcd ( int a , int b , int &x, int &y )
{
	if ( !b ) return x = 1 , y = 0 , void();
	exgcd ( b , a % b , y , x ) , y -= ( a / b ) * x;
}

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr) , cout.tie(nullptr);
	int T = read();	
	while ( T -- )
	{
		int a = read() , b = read() , c = read() , x = 0 , y = 0 , cnt = 0 , xmin , xmax , ymin , ymax;
		int g = gcd ( a , b );
		if ( c % g ^ 0 ) { cout << -1 << endl; continue; }
		a /= g , b /= g , c /= g , exgcd ( a , b , x , y );
		x *= c , y *= c; // 求出一组特解
		xmin = ( x > 0 && x % b != 0 ) ? x % b : x % b + b;
		ymax = ( c - xmin * a ) / b;
		ymin = ( y > 0 && y % a != 0 ) ? y % a : y % a + a;
		xmax = ( c - ymin * b ) / a;
		if ( xmax > 0 ) cnt = ( xmax - xmin ) / b + 1;
		if ( cnt ) cout << cnt << ' ' << xmin << ' ' << ymin << ' ' << xmax << ' ' << ymax << endl;
		else cout << xmin << ' ' << ymin << endl;
	}
	return 0;
}

Enlarge GCD

首先我们消除原来的gcd对于序列的影响 所以我们将序列中的所有数先除gcd 然后我们的目标就是让序列中的所有数的gcd大于1 将新序列上的数标记为1

所以我们可以用埃筛的性质 从小到大枚举质数作为这个gcd 判断所有数 计入能整除这个数的数的个数 最后将不能整除的数与ansmin

值域1.5e7而不是1e7

#include <bits/stdc++.h>
using namespace std;
#define mid (l+r>>1)
#define endl '\n'
#define inl inline
#define ls(p) t[p].son[0]
#define rs(p) t[p].son[1]
#define lson ls(p),l,mid
#define rson rs(p),mid+1,r
#define eb emplace_back
const int N = 1.5e7 + 5;
const int inf = 0x3f3f3f3f;
//char buf[1<<24] , *p1 , *p2;
//#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<24,stdin),p1==p2)?EOF:*p1++)
#define getchar() cin.get();
int read()
{
	int x = 0 , f = 1;
	char ch = getchar();
	while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
	while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
	return x * f ;
}

int n , a[N] , ans = inf , maxx , vis[N] , cnt[N];
int gcd ( int a , int b )
{
	if ( a % b == 0 ) return b;
	return gcd ( b , a % b );
}

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr) , cout.tie(nullptr);
	n = read();
	for ( int i = 1 ; i <= n ; i ++ ) a[i] = read();
	int gcdd = a[1];
	for ( int i = 2 ; i <= n ; i ++ ) gcdd = gcd ( gcdd , a[i] );
	for ( int i = 1 ; i <= n ; i ++ ) a[i] /= gcdd , cnt[a[i]] ++ , maxx = max ( a[i] , maxx );
	for ( int i = 2 ; i <= maxx ; i ++ )
	{
		int res = 0;
		if ( !vis[i] ) for ( int j = 1 ; j <= maxx / i ; j ++ ) vis[i*j] = 1 , res += cnt[i*j];
		ans = min ( ans , n - res );
	}
	cout << ( ans == inf ? -1 : ans ) << endl;
	return 0;
}

P2265 路边的水沟

因为我们从走对角线的长度就是曼哈顿距离n+m 那么我们相当于是从n+m步中找出m步向左的步数

组合数即可

#include <bits/stdc++.h>
using namespace std;
#define mid (l+r>>1)
#define endl '\n'
#define inl inline
#define ls(p) t[p].son[0]
#define rs(p) t[p].son[1]
#define lson ls(p),l,mid
#define rson rs(p),mid+1,r
#define eb emplace_back
#define int long long 
const int N = 1e6 + 5;
const int maxn = 1e6;
const int mod = 1e9 + 7;
char buf[1<<24] , *p1 , *p2;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<24,stdin),p1==p2)?EOF:*p1++)
//#define getchar() cin.get();
int read()
{
	int x = 0 , f = 1;
	char ch = getchar();
	while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
	while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
	return x * f ;
}

int n , m , inv[N] , fac[N] , ifac[N];

void init()
{
	ifac[0] = ifac[1] = inv[1] = fac[0] = fac[1] = 1;
	for ( int i = 2 ; i <= maxn ; i ++ )
	{
		fac[i] = fac[i-1] * i % mod;
		inv[i] = ( mod - mod / i ) * inv[mod%i] % mod;
		ifac[i] = ifac[i-1] * inv[i] % mod;
	}
}

int C ( int n , int m ) { return fac[n] * ifac[n-m] % mod * ifac[m] % mod; }

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr) , cout.tie(nullptr);
	n = read() , m = read();
	init();
	cout << C ( n + m , m ) << endl;
	return 0;
}

CRT&exCRT

P3868 [TJOI2009] 猜数字

标准的crt 需要开int128

#include <bits/stdc++.h>
using namespace std;
#define mid (l+r>>1)
#define endl '\n'
#define inl inline
#define eb emplace_back
#define int __int128
const int N = 15 + 5;
char buf[1<<24] , *p1 , *p2;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<24,stdin),p1==p2)?EOF:*p1++)
//#define getchar() cin.get();
int read()
{
	int x = 0 , f = 1;
	char ch = getchar();
	while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
	while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
	return x * f ;
}

int n , M = 1 , a[N] , b[N] , k[N] , ans;//余数 模数

void exgcd ( int aa , int bb , int &x , int &y )
{
	if ( !bb ) return x = 1 , y = 0 , void();
	exgcd ( bb , aa % bb , y , x ) , y -= aa / bb * x;
}

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr) , cout.tie(nullptr);
	n = read();
	for ( int i = 1 ; i <= n ; i ++ ) a[i] = read();
	for ( int i = 1 ; i <= n ; i ++ ) b[i] = read() , M *= b[i];
	for ( int i = 1 ; i <= n ; i ++ ) k[i] = M / b[i];
	for ( int i = 1 ; i <= n ; i ++ )
	{
		int x = 0 , y = 0;
		exgcd ( k[i] , b[i] , x , y );
		ans = ( ( ans + k[i] * a[i] * x ) % M + M ) % M;
	}
	cout << (long long)ans << endl;	
	return 0;
}

欧拉函数

P2158 [SDOI2008] 仪仗队

对于一个4×4的东西 我们需要算这些1中的答案(我们以(0,0)为坐标原点)

1110
1100
1000
0000

对于一个点(x,y) 它被看到的充要条件就是x,y互质 因为如果不互质 可以通过在两边同时除gcd(x,y)的方法使它重复 所以这个东西不能被计入

这个东西正好符合欧拉函数的定义 那么上半部分答案就是ans=i=1n1ϕ(i)

对于欧拉函数的求法 可以线性筛求解 分三种情况:

  1. i为质数 p[i]=i1
  2. ji互质 p[j]=p[i](prime[j]1)
  3. ji不互质 p[j]=p[i]prime[j]

最后我们需要加上(1,1)这个点的答案 即2×ans+1

完整代码:

#include <bits/stdc++.h>
using namespace std;
#define mid (l+r>>1)
#define endl '\n'
#define inl inline
#define eb emplace_back
const int N = 4e4 + 5;
char buf[1<<24] , *p1 , *p2;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<24,stdin),p1==p2)?EOF:*p1++)
//#define getchar() cin.get();
int read()
{
	int x = 0 , f = 1;
	char ch = getchar();
	while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
	while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
	return x * f ;
}

int ans , n , prime[N] , tot , p[N] , isntprime[N];

void phi()
{
	p[1] = 1;
	for ( int i = 2 ; i <= n ; i ++ )
	{
		if ( !isntprime[i] ) prime[++tot] = i , p[i] = i - 1;
		for ( int j = 1 ; j <= tot && i * prime[j] <= n ; j ++ )
		{
			isntprime[i*prime[j]] = 1;
			if ( i % prime[j] == 0 ) { p[i*prime[j]] = p[i] * prime[j]; break; }
			else p[i*prime[j]] = p[i] * ( prime[j] - 1 );
		}
	}
}

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr) , cout.tie(nullptr);
	n = read();
	phi();
	for ( int i = 1 ; i < n ; i ++ ) ans += p[i];
	cout << ( n != 1 ? ans * 2 + 1 : 0 ) << endl; 
	return 0;
}

P5091 【模板】扩展欧拉定理

欧拉定理:

a,mZ,且 gcd(a,m)=1 时有:

aφ(m)1(modm)

这里 φ(x) 是数论中的欧拉函数。

所以 ababmodφ(m)(modm)


扩展欧拉定理:

a,mZ 时有:
ab{ab,b<φ(m)abmodφ(m)+φ(m),bφ(m)(modm)


O(n)

柿子:φ(a)=a×(11p1)(11p2)×...×(11pn) 其中p1pka的唯一分解

枚举因数到n并筛除即可


本题分析:

观察到b很大 我们可以重载read()函数 使得它可以边读入大整数b边取模

必须注意 如果b<φ(m)的话 是不需要加上φ(m)的 这一点需要特判

读入进合理范围的b之后 我们可以用快速幂求解

#include <bits/stdc++.h>
using namespace std;
#define mid (l+r>>1)
#define endl '\n'
#define inl inline
#define eb emplace_back
#define int long long
char buf[1<<24] , *p1 , *p2;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<24,stdin),p1==p2)?EOF:*p1++)
//#define getchar() cin.get();
int read()
{
	int x = 0 , f = 1;
	char ch = getchar();
	while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
	while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
	return x * f ;
}

int read ( int mod )
{
	int x = 0 , f = 0; char ch = getchar();
	while ( !isdigit(ch) ) ch = getchar();
	while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); if ( x >= mod ) f = 1 , x %= mod; ch = getchar(); }
	return x + (f?mod:0);
}

int a , b , m;

int phi ( int n )
{
	int ans = n;
	for ( int i = 2 ; i * i <= n ; i ++ )
		if ( n % i == 0 ) 
		{
			ans = ans / i * ( i - 1 );
			while ( n % i == 0 ) n /= i;
		}
	if ( n > 1 ) ans = ans / n * ( n - 1 );
	return ans;
}

int ksm ( int base , int k , int mod )
{
	int res = 1;
	for ( ; k ; k >>= 1 , base = base * base % mod )
		if ( k & 1 ) res = res * base % mod;
	return res;
}

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr) , cout.tie(nullptr);
	a = read() , m = read();
	b = read(phi(m));
	cout << ksm ( a , b , m ) << endl;
	return 0;
}

P4139 上帝与集合的正确用法

根据扩展欧拉定理 当b>φ(p)abab mod φ(p)+φ(p)(mod p) 其中a,p可以不互质

所以要求22222...(mod p) 就相当于求2(222...mod φ(p)+φ(p))(mod p) 对于指数我们可以递归求解 同时需要线性筛预处理φ(p)

#include <bits/stdc++.h>
using namespace std;
#define mid (l+r>>1)
#define endl '\n'
#define inl inline
#define eb emplace_back
const int N = 1e7 + 5;
const int maxn = 1e7;
char buf[1<<24] , *p1 , *p2;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<24,stdin),p1==p2)?EOF:*p1++)
//#define getchar() cin.get();
int read()
{
	int x = 0 , f = 1;
	char ch = getchar();
	while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
	while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
	return x * f ;
}

int ans , n , prime[N] , tot , p[N] , isntprime[N];

void phi ()
{
	p[1] = 1;
	for ( int i = 2 ; i <= maxn ; i ++ )
	{
		if ( !isntprime[i] ) prime[++tot] = i , p[i] = i - 1;
		for ( int j = 1 ; j <= tot && i * prime[j] <= maxn ; j ++ )
		{
			isntprime[i*prime[j]] = 1;
			if ( i % prime[j] == 0 ) { p[i*prime[j]] = p[i] * prime[j]; break; }
			else p[i*prime[j]] = p[i] * ( prime[j] - 1 );
		}
	}
}

long long pow ( long long base , int k , int mod )
{
	long long res = 1;
	while ( k )
	{
		if ( k & 1 ) ( res *= base ) %= mod;
		( base *= base ) %= mod;
		k >>= 1;
	}
	return res;
}

int solve ( int x )
{
	if ( x == 1 ) return 0;
	return pow ( 2 , solve ( p[x] ) + p[x] , x );
}

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr) , cout.tie(nullptr);
	phi();
	int T = read();
	while ( T -- )
	{
		int x = read();
		cout << solve(x) << endl;
	}
	return 0;
}
posted @   Echo_Long  阅读(11)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示