数论·杂

欧几里得(辗转相除)

int gcd(int a, int b) {
	return b == 0 ? a : gcd(b, a % b);
} //递归版
int gcd(int a, int b) {
	if(a < b) swap(a, b);
	int c; 
	while(b) c = a % b, a = b, b = c;
	return a;
} //循环版

扩展欧几里得

int exgcd(int a, int b, int &x, int &y) {
    if(b == 0) { x = 1, y = 0, return a; }
    int ans = exgcd(b, a % b, x, y);
    int oo = x;
    x = y, y = oo - a / b * y;
    return ans; //a和b的最大公因数
}

逆元

//扩展欧几里得求逆元
int ny(int a, int m) {
    int x, y;
    int gcd = exgcd(a, m, x, y);
    if(1 % gcd != 0) return -1;
    x *= 1 / gcd;
    m = abs(m);
    int ans = x % m;
    if(ans <= 0) ans += m;
    return ans;
}
//费马小定理求逆元
ll ksm(ll a, ll b, ll p) {
    ll res = 1;
    while(b) {
        if(b & 1) res = res * a % p;
        a = a * a % p;
        b >> 1;
    }
    return res;
}

ll fm(ll a, ll p) { return ksm(a, p - 2, p); }
//线性递推求逆元
int inv[N];
void ny(int p) {
    inv[1] = 1;
    for(int i = 2; i < p; i ++)
        inv[i] = (p - p / i) * inv[p % i] % p;
}
//欧拉定理求逆元
int phi(int x) {
    int ans = x;
    for(int i = 2; i * i <= x; i ++) {
        if(x % i == 0) {
            ans = ans / i * (i - 1);
            while(x % i == 0) x /= i;
        }
    }
    if(x > 1) ans = ans / x * (x - 1);
    return ans;
}

中国剩余定理

#include<cstdio>
#define ll long long
ll n, a[16], m[16], M, mul = 1, res;

void exgcd(ll a, ll b, ll &x, ll &y) {
    if(b == 0) { x = 1; y = 0; return; }
    exgcd(b, a % b, x, y);
    int z = x; x = y, y = z - y * (a / b);
}
int main() {
    cin >> n;
    for(int i = 1; i <= n; i ++) {
        cin >> m[i] >> a[i];
        mul *= m[i];
    } 
    for(int i = 1; i <= n; i ++) {
        M = mul / m[i];
        ll x = 0, y = 0;
        exgcd(M, m[i], x, y);
        res += a[i] * M * (x < 0 ? x + m[i] : x);
    }
    cout << res % mul;
    return 0;
}

组合数打表

#include <iostream>
#define LL long long;
using namespace std;      
const LL mod = 1e9+7;     //大质数P
const LL N = 1e6+5;       //n的范围
LL fac[N], inv[N];	  //阶乘数组 阶乘逆元

LL power(LL a, int b) {
	LL res = 1;
	while(b) {
		if(b & 1) res = res * a % mod;
		a = a * a % mod;
		b >>= 1;
	}
	return res;
}
void Init()	{	//这里给阶乘及逆元打表,以备让你求多次组合数
	fac[0] = 1;     
	for(int i = 1; i < N; i++)
		fac[i] = fac[i - 1] * i % mod;
	inv[N - 1] = power(fac[N - 1], mod - 2);
	for(int i = N - 2; i >= 0; i ++) inv[i] = inv[i+1] * (i+1) % mod;
} 
int main() {
	int m, n;
	cin >> m >> n;
	LL res = fac[n] * inv[m] % mod *inv[n-m] % mod;
	cout << res;
	return 0;
}

一些题

P2303 [SDOI2012] Longge 的问题

简化题意:给定n,求 \(\displaystyle\sum^n_{i=1}gcd(i,n)\)

数据范围\(1\le n\le2^{32}\)

解题思路

\[\displaystyle\sum^n_{i=1}gcd(i,n)\\=\sum_{j=1}^n(j*\sum^n_{i=1}[gcd(i,n)=j])\\=\sum^n_{j=1}(j*\sum^n_{i=1}[gcd(i/j,n/j)=1](j|i,j|n))\\ \]

#include<cstdio>
using namespace std;
typedef long long ll;

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

int main() {
	scanf("%lld", &n);
	printf("%lld", fi());
	return 0;
}

P2155 [SDOI2008]沙拉公主的困惑

简化题意:给定T,R,T组数据,R为质数,每组给定N,M,求1~N!中与M!互质的数的个数,结果对R取模

数据范围\(1\le M\le N\le10~000~000,1\le T\le10~000,2\le R\le1000~000~000+10\)

解题思路

\[ans=\frac{n!}{m!}\varphi(m!)\\=\frac{n!}{m!}m!\prod_{p|m}(1-\frac1p)\\=n!\prod_{p|m}(1-\frac1p)\\=n!\frac{\prod(p-1)}{\prod p} \]

其中 \(p\le M\)\(p\) 为质数

然后处理 \(n!\)\(\prod(p-1)\)\(\prod p\)\(R\) 的逆元

考虑到 \(n!\) 中的因子 \(R\) 可能和 \(\prod p\) 中的 \(R\) 约掉,所以对 \(n \ge R\)\(n!\) 消去一个 \(R\),对 \(M \ge R\)\(\prod p\) 同时消去一个 \(R\)

#include <bits/stdc++.h>
#define N 10000000
using namespace std;

bool tag[N + 1];
int T, R, n, m, cnt;
int pri[N], inv[N + 1], pi[N], p[N], nn[N + 1], pos[N + 1];

void Euler() {
	for(int i = 2; i <= N; i ++) {
		if(!tag[i]) pri[++ cnt] = i;
		for(int j = 1; j <= cnt and pri[j] * i <= N; j ++) {
			tag[pri[j] * i] = 1;
			if(i % pri[j] == 0) break;
		}
	}
}

void work() {
	inv[1] = pi[0] = p[0] = nn[0] = 1;
	for(int i = 2; i < R and i <= N; i ++) //线性求逆元 
		inv[i] = 1ll * (R - R / i) * inv[R % i] % R;
		
	for(int i = 1; i <= cnt; i ++) // $\prod(p - 1)$ 
		pi[i] = 1ll * pi[i - 1] * (pri[i] - 1) % R;
	
	for(int i = 1; i <= cnt; i ++) // $\prod p$
		if(pri[i] != R) p[i] = 1ll * p[i - 1] * inv[pri[i] % R] % R;
		else p[i] = p[i - 1];
	
	for(int i = 1; i <= N; i ++) // n!
		if(i != R) nn[i] = 1ll * nn[i - 1] * i % R;
		else nn[i] = nn[i - 1];  
	
	for(int i = 2; i <= N; i ++) //位置 
		if(tag[i]) pos[i] = pos[i - 1];
		else pos[i] = pos[i - 1] + 1;
}

int main() {
	cin >> T >> R;
	Euler();
	work();
	while(T --) {
		cin >> n >> m;
		if(n >= R and m < R) cout << 0 << "\n";
		else cout << 1ll * nn[n] * pi[pos[m]] % R * p[pos[m]] % R << "\n";
	}
	return 0;
}			

P4345 [SHOI2015]超能粒子炮·改

简化题意:t组数据,每组给定n,k,求 \(\displaystyle\sum_{i=0}^kC^i_n\%2333\)

数据范围\(1\le t\le100~000;n,k\le 10^{18}\)

解题思路:用到了卢卡斯定理

\[C^m_n\%p=C^{m/p}_{n/p}*C^{m\%p}_{n\%p}\%p \]

\[f(n,k)=\sum^k_{i=0}C^i_n\\=\sum^k_{i=0}C^{i/p}_{n/p}*C^{i\%p}_{n\%p}\%p\\=\sum^{p-1}_{i=0}C^i_{n\%p}*(C^0_{n/p}+C^1_{n/p}+\dots+C^{k/p-1_{n/p}})\\=f(n\%p,p-1)*f(n/p,k/p-1) \]

分块处理

#include<bits/stdc++.h>
#define N 2333
using namespace std;
typedef long long ll;
ll n, k, t, c[N + 5][N + 5], f[N + 5][N + 4];

ll lucas(ll x, ll y) {
	if(x < y) return 0; 
	if(x < N and y < N) return c[x][y];
	return lucas(x / N, y / N) * c[x % N][y % N] % N;
}

ll ovo(ll n, ll k) {
	if(k < 0) return 0;
	if(!n or !k) return 1;
	if(n < N and k < N) return f[n][k];
	return (ovo(n / N, k / N - 1) * f[n % N][N - 1] + lucas(n / N, k / N) * f[n % N][k % N]) % N;
}
 
int main() {
	cin >> t;
	c[0][0] = 1;
	for(int i = 1; i <= N + 2; i ++) c[i][i] = c[i][0] = 1;
	for(int i = 1; i <= N + 2; i ++) 
	  for(int j = 1; j < i; j ++)
	    c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % N;
	    
	f[0][0] = 1;
	for(int i = 1; i <= N + 2; i ++) f[i][0] = 1;
	for(int i = 0; i <= N + 2; i ++) 
	  for(int j = 1; j <= N + 2; j ++)
	  	f[i][j] = (c[i][j] + f[i][j - 1]) % N;
	  	
	while(t --) {
		cin >> n >> k;
	 	cout << ovo(n, k) << "\n";
	}
	return 0;
}
posted @ 2020-08-27 21:12  hulne  阅读(103)  评论(1编辑  收藏  举报