8.7 数论专题
\(exgcd\)
P5656 【模板】二元一次不定方程 (exgcd)
可以先用\(exgcd\)求出一组特解
然后再搞出最小的正整数\(x\) 用\(x\)来推最大的\(y\) 再用最小的正整数\(y\)来推最大的\(x\)即可
最后通过\((xmax-xmin)/b+1\)来获取解的个数 如果没有正整数解就输出\(xmin\)和\(ymin\)(因为已经保证这两个东西都大于\(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\) 判断所有数 计入能整除这个数的数的个数 最后将不能整除的数与\(ans\)取\(min\)
值域\(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\times 4\)的东西 我们需要算这些\(1\)中的答案(我们以(\(0,0\))为坐标原点)
1110
1100
1000
0000
对于一个点\((x,y)\) 它被看到的充要条件就是\(x,y\)互质 因为如果不互质 可以通过在两边同时除\(gcd(x,y)\)的方法使它重复 所以这个东西不能被计入
这个东西正好符合欧拉函数的定义 那么上半部分答案就是\(ans=\sum_{i=1}^{n-1}\phi(i)\)
对于欧拉函数的求法 可以线性筛求解 分三种情况:
- \(i\)为质数 \(p[i]=i-1\)
- \(j\)与\(i\)互质 \(p[j]=p[i]*(prime[j]-1)\)
- \(j\)与\(i\)不互质 \(p[j]=p[i]*prime[j]\)
最后我们需要加上\((1,1)\)这个点的答案 即\(2\times 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,m\in \mathbb{Z}\),且 \(\gcd(a,m)=1\) 时有:
\(a^{\varphi(m)}\equiv 1\pmod{m}\)。
这里 \(\varphi(x)\) 是数论中的欧拉函数。
所以 \(a^b\equiv a^{b\bmod \varphi(m)}\pmod m\)。
扩展欧拉定理:
当 \(a,m\in \mathbb{Z}\) 时有:
\(a^b\equiv\left\{\begin{matrix}a^b&,b<\varphi(m)\\a^{b\bmod\varphi(m)+\varphi(m)}&,b\ge\varphi(m)\end{matrix}\right.\pmod m\)。
\(O(\sqrt n)时间复杂度内求一个数的欧拉函数\)
柿子:\(\varphi(a)=a\times (1-\frac {1}{p_1})\cdot (1-\frac {1}{p_2})\times ...\times(1-\frac {1}{p_n})\) 其中\(p_1 \sim p_k\)为\(a\)的唯一分解
枚举因数到\(\sqrt n\)并筛除即可
本题分析:
观察到\(b\)很大 我们可以重载\(read()\)函数 使得它可以边读入大整数\(b\)边取模
必须注意 如果\(b<\varphi(m)\)的话 是不需要加上\(\varphi(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>\varphi(p) $ 有\(a^b\equiv a^{b\ mod\ \varphi(p)+\varphi(p)}(mod\ p)\) 其中\(a,p\)可以不互质
所以要求\(2^{2^{2^{2^{2^{...}}}}}(mod\ p)\) 就相当于求\(2^{({2^{2^{2^{...}}}mod\ \varphi(p)+\varphi(p)})}(mod\ p)\) 对于指数我们可以递归求解 同时需要线性筛预处理\(\varphi(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;
}

浙公网安备 33010602011771号