「数论全家桶」Strange Way to Express Integers + 古代猪文 + 礼物

三道题都是比较裸的题,主要是记录一下板子。

Strange Way to Express Integers

非常裸的EXCRT,注意一些小细节。

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
const int maxn = 1e5 + 5;
typedef long long ll;
ll a[maxn], m[maxn];
ll x, i, j, d, M;
ll gcd(ll a, ll b) {
    if (b == 0) return a;
    if (b == 1) return 1;
    return gcd(b, a % b);
}
ll lcm(ll a, ll b) {
    return a * b / gcd(a, b);
}
ll exgcd(ll a, ll b, ll &x, ll &y) {
    if (b == 0) { x = 1; y = 0; return a; }
    ll d = exgcd(b, a % b, x, y);
    ll k = x;
    x = y, y = k - y * (a / b);
    return d;
}
int main() {
    int n;
    while (~scanf("%d", &n)) {
	for (int s = 1; s <= n; s++) scanf("%lld %lld", &m[s], &a[s]);
	M = m[1];
	x = a[1];
	ll d;
	int flag = 1;
	for (int s = 2; s <= n; s++) {
	    d = exgcd(M, m[s], i, j);
	    if ((a[s] - x) % d) {
		printf("-1\n");
		flag = 0;
		break;
	    }
	    i = i * ((a[s] - x) / d);
            //将i调整到合适的范围
	    i = (i % m[s] + m[s]) % m[s];
            //这里不要对x取模,否则会挂的很惨
	    x = x + i * M;
	    M = lcm(M, m[s]);
	}
	if (flag) printf("%lld\n", x % M);
    }
    return 0;
}

古代猪文

欧拉定理内容

如果 \(\gcd(a,p) = 1\),那么 \(a^{\varphi(p)} \equiv 1 \pmod{p}\)

扩展

在模\(p\)意义下

  1. \(a^b \equiv a^{b \bmod \varphi(p)}\)\(\gcd(a,p) = 1\)
  2. \(a^b \equiv a^{(b \bmod \varphi(p))+\varphi(p)}\)\(\gcd(a,p)\ne 1, \varphi(p) \leq b\)

然后看这道题,题意是求 \(g^{\sum_{k|n}{n \choose k}} \mod 999911659\)

直接做显然会指数爆炸,然后考虑用扩展欧拉定理对指数取模,发现999911659是素数,于是原式变为 \(g^{\sum_{k|n}{n \choose k} \mod 999911658} \mod 999911659\)

问题转化为求 \(\sum_{k|n}{n \choose k} \mod 999911658\),发现999911658刚好能拆成4个素数因子相乘,可以用lucas+crt解决,否则就要像下面的礼物一样用exlucas了。

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <iostream>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 5;
ll a[5];
ll jc[maxn];
ll Qpow(ll x, ll t, ll p) {
    x %= p;
    ll s = 1;
    while (t) {
	if (t & 1) s = s * x % p;
	x = x * x % p;
	t >>= 1;
    }
    return s;
}
ll C(ll n, ll m, ll p) {
    if (n < m) return 0;
    if (m == 0) return 1;
    return jc[n] * Qpow(jc[m], p - 2, p) % p * Qpow(jc[n - m], p - 2, p) % p;
}
ll Lucas(ll n, ll m, ll p) {
    if (m == 0) return 1;
    return C(n % p, m % p, p) * Lucas(n / p, m / p, p) % p;
}
ll n, g;
ll factor[maxn], p[5] = { 0, 2, 3, 4679, 35617 };
ll Crt() {
    ll M = 1;
    for (int i = 1; i <= 4; i++) M *= p[i];
    ll ans = 0;
    for (int i = 1; i <= 4; i++)
	ans = (ans + a[i] * (M / p[i]) % M * Qpow(M / p[i], p[i] - 2, p[i]) % M) % M;
    return ans;
}
int main() {
    scanf("%lld %lld", &n, &g);
    if (g % 999911659 == 0) return printf("0\n"), 0;
    for (int i = 1; i * i <= n; i++) {
	if (n % i == 0) {
	    factor[++factor[0]] = i;
	    if (n / i != i) factor[++factor[0]] = n / i;
	}
    }
    for (int i = 1; i <= 4; i++) {
	jc[0] = 1;
	for (int j = 1; j <= p[i]; j++) jc[j] = jc[j - 1] * j % p[i];
	for (int j = 1; j <= factor[0]; j++)
	    a[i] = (a[i] + Lucas(n, factor[j], p[i])) % p[i];
    }
    ll x = Crt();
    printf("%lld\n", Qpow(g, x, 999911659));
    return 0;
}

礼物

题意为

\[\prod_{i=1}^{n} {{n - \sum_{j=1}^{i-1}w[j]} \choose w[i]} \mod p \]

问题在于 \(p\),不一定是质数,而且分解质因子后每个质因子的指数也不一定都是1,所以用exlucas求解每个分问题,注意求逆元因为模数不一定是质数不能用费马小定理,因为每个模数一定互质,最后crt合并即可。

附exlucas讲解:学exlucas这一篇就够了,吹爆Midoria7%%%

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>
#include <cmath>
using namespace std;
const int maxn = 20;
typedef long long ll;

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

ll Inv(ll a, ll b) {
    ll x, y;
    Ex_gcd(a, b, x, y);
    return (x % b + b) % b;
}

ll Qpow(ll x, ll t, ll mod) {
    ll s = 1;
    while (t) {
	if (t & 1) s = s * x % mod;
	x = x * x % mod;
	t >>= 1;
    }
    return s;
}

ll Fac(ll n, ll p, ll pk) {
    if (!n) return 1;
    ll res = 1;
    for (ll i = 1; i <= pk; i++) if (i % p) res = res * i % pk;
    res = Qpow(res, n / pk, pk);
    for (ll i = 1; i <= n % pk; i++) if (i % p) res = res * i % pk;
    return res * Fac(n / p, p, pk) % pk;
}

ll C(ll n, ll m, ll p, ll pk) {
    ll x = 0, y = 0, z = 0;
    for (ll i = p; i <= n; i *= p) x += n / i;
    for (ll i = p; i <= m; i *= p) y += m / i;
    for (ll i = p; i <= n - m; i *= p) z += (n - m) / i;
    return (Fac(n, p, pk) * Inv(Fac(m, p, pk), pk) % pk * Inv(Fac(n - m, p, pk), pk) % pk * Qpow(p, x - y - z, pk) % pk) % pk;
}

ll Crt(ll n, ll a[], ll mod[]) {
    ll M = 1, res = 0;
    for (int i = 1; i <= n; i++) M *= mod[i];
    ll w, x, y;
    for (int i = 1; i <= n; i++) {
	w = M / mod[i];
	res = (res + a[i] * Inv(w, mod[i]) * w % M) % M;
    }
    return res;
}

ll Ex_lucas(ll n, ll m, ll P) {
    ll mod[20], a[20], cnt = 0;
    for (ll i = 2; i * i <= P; i++) {
	if (P % i == 0) {
	    mod[++cnt] = 1;
	    while (P % i == 0) {
		mod[cnt] *= i;
		P /= i;
	    }
	    a[cnt] = C(n, m, i, mod[cnt]);
	}
    }
    if (P > 1) mod[++cnt] = P, a[cnt] = C(n, m, P, P);
    return Crt(cnt, a, mod);
}

#define cnt pk[0]
ll ans[maxn], pk[maxn];

int main() {
    ll n, m, p, w[10];
    scanf("%lld %lld %lld", &p, &n, &m);
    for (int i = 1; i <= m; i++) scanf("%lld", &w[i]);
    for (ll i = 2; i * i <= p; i++) {
	if (p % i == 0) {
	    pk[++cnt] = 1;
	    while (p % i == 0) {
		pk[cnt] *= i;
		p /= i;
	    }
	}
    }
    if (p > 1) pk[++cnt] = p;
    for (int i = 1; i <= cnt; i++) ans[i] = 1;
    ll sum = 0;
    for (int i = 1; i <= m; i++) sum += w[i];
    if (sum > n) return printf("Impossible\n"), 0;
    sum = 0;
    for (int i = 1; i <= m; i++) {
	for (int j = 1; j <= cnt; j++) {
	    ans[j] = (ans[j] * Ex_lucas(n - sum, w[i], pk[j]) % pk[j]) % pk[j];
	}
	sum += w[i];
    }
    printf("%lld\n", Crt(cnt, ans, pk));
    return 0;
}
posted @ 2020-10-02 15:03  zfio  阅读(124)  评论(1编辑  收藏  举报