zoukankan      html  css  js  c++  java
  • luogu P5605 小 A 与两位神仙

    题目传送门

      传送门

    Subtask 1

      直接模拟。

    Subtask 2

      BSGS算法模板。

    Subtask 3

      考虑模 $m$ 的任意一个原根 $g$。

      假设 $g^{ra} equiv x pmod {m}, g^{rb} equiv y pmod{m}$ 。

      那么原题的方程等价于方程 $a cdot ra equiv rb pmod {varphi(m)}$。

      它等价于 $x cdot ra - ycdot varphi(m) = rb$。

      设 $d = varphi(m)$。

      它存在满足条件的解当且仅当 $(ra, d) mid rb$。

      这个条件仍然不好处理,因为我们难以求出 $rb$。

      考虑这样一个条件 $(ra, d) mid (rb, d)$。

      我们来证明,满足 $(ra, d) mid rb$ 当且仅当 $(ra, d) mid (rb, d)$。

      充分性显然。

      考虑必要性,因为 $((ra,d), (rb,d)) = (ra, rb, d) = ((ra, d) , rb) = (ra, d)$。

      所以有 $(ra, d) mid (rb, d)$。

      现在的问题是如何求 $(ra, d)$。

    引理 设 $g$ 为模 $m$ 意义下的一个原根 ,如果 $g^{ra} equiv a pmod {m}$,设 $delta_m(a) = d$ ,那么有 $(ra, varphi(m)) d = varphi(m)$.

      证明 因为有 $left( g^{ra} ight)^d equiv 1pmod{m}$,所以 $varphi(m) | ra cdot d$。

      那么有 $ra cdot d equiv 0 pmod {varphi(m)}$.

      所以有 $d equiv 0 pmod{frac{varphi(m)}{(varphi(m), ra)}}$.

      因为 $d$ 是满足条件的最小正整数,所以有 $d = frac{varphi(m)}{(varphi(m), ra)}$。

      所以 $(varphi(m), ra) d = varphi(m)$。

      因此我们把问题转化为求阶。

      众所周知,阶是 $varphi(m)$ 的约数,所以我们暴力枚举所有因子能通过这个subtask。

    Subtask 4

      因为 $p$ 随机,所以期望的因子个数只有几千,做法同 Subtask 3。

    Subtask 5

      骗暴力选手去卡常。

    Subtask 6

      我们考虑优化求阶的做法。

    引理 设 $d = delta_m(x)$,如果 $d_0$ 满足 $x^{d_0} equiv 1pmod {m}$,那么有 $d | d_0$。

      证明 设 $d_0 = qd + r (0 < r < d)$。那么有 $x^{qd + r} equiv (x^{d})^q x^r equiv x^r equiv 1pmod{m}$。因为 $d$ 是最小的正整数满足 $x^{d} equiv 1 pmod {m}$,但 $r$ 是更小的满足条件的正整数,这和阶的定义矛盾。

      初始令 $d =varphi(m)$。根据欧拉定理,我们知道一定有 $x^{d} equiv 1 pmod{m}$。设 $d = kdelta_m(x)$

      考虑枚举任意一个 $varphi(m)$ 的质因子 $p$,如果满足 $p mid d$,并且 $x^{d / p} equiv 1 pmod {m}$。 那么有 $p mid k$,我们令 $d' = d / p$ 继续执行这个过程,直到不存在满足条件的 $p$。

      由于乘法取模我们可以使用 __int128 ,所以时间复杂度 $O(T log^2 V + n^{1/4})$。

      如果同时求 $(ra, varphi(m)), (rb, varphi(m))$ 可能会被卡常。

    Subtask 7

      我们注意到,我们没有必要求 $(rb, varphi(m))$。

      设 $d = varphi(m)$。

      $(ra, d) | (rb, d) $ 等价于 $frac{d}{(rb, d)}| frac{d}{(ra, d)}$。

      因此我们只用判断 $b^{e}$ 模 $m$ 的余数是否为1就行了。

      其中 $e$ 是最小的正整数满足 $a^e equiv 1 pmod{m}$,即 $a$ 模 $m$ 的阶。

    吐槽

    • 不知道为什么很多人觉得 $(ra, d) mid rb$ 当且仅当 $(ra, d) mid (rb, d)$ 非常地显然,可能是我数学比较菜,觉得不是那么显然,需要说明一下。
    • 成功区分了会求阶和不会求阶的选手
    • 开始想卡掉求 2 次阶的做法,因为 F 很难,为了降低比赛难度,所以被要求这里不卡常,不过好像卡不卡区分度都是一样的。
    • 事后发现,二进制分组和 long double 快速乘均可以轻松跑进两倍标程序时限内,感觉我对卡常一无所知,我似乎还忘了卡 long double 快速乘。(虽然我并不知道能个不能卡)两倍常数还想卡?
    • 洛谷怎么最大只支持 100 组数据

    Code

    #include <bits/stdc++.h>
    using namespace std;
    typedef bool boolean;
    
    #define ll long long
    #define ull unsigned long long
    
    template <typename T>
    T add(T a, T b, T m) {
    	return ((a += b) >= m) ? (a - m) : (a);
    }
    
    template <typename T>
    void pcopy(T* pst, const T* ped, T* pval) {
    	for ( ; pst != ped; *(pst++) = *(pval++));
    }
    
    ll mul(ll a, ll b, ll m) {
    	return ((__int128)a) * b % m;
    /*	ll rt = 0, pa = a;
    	for ( ; b; b >>= 1, pa = add(pa, pa, m))
    		if (b & 1)
    			rt = add(rt, pa, m);
    	return rt; */
    }
    
    ll qpow(ll a, ll p, ll m) {
    	ll rt = 1, pa = a;
    	for ( ; p; p >>= 1, pa = mul(pa, pa, m))
    		if (p & 1)
    			rt = mul(rt, pa, m);
    	return rt;
    }
    
    ll gcd(ll a, ll b) {
    	return (b) ? (gcd(b, a % b)) : (a);
    }
    
    ll randLL() {
    	static ull seed = 998244353, msk = (1ull << 61) - 1;
    	return (signed ll) ((seed = seed * seed + seed * 3 + 233) & msk);
    }
    
    boolean miller_rabin(ll n) {
    	static int T = 25;
    	static const int pri[10] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29};
    	if (!(n & 1))
    		return n == 2;
    	if (n < 1000) {
    		for (int p = 2; p * p <= n; p++)
    			if (!(n % p))
    				return false;
    		return true;
    	}
    	for (int i = 0; i < 10; i++) {
    		if (n == pri[i]) {
    			return true;
    		} else if (!(n % pri[i])) {
    			return false;
    		}
    	}
    	ll d = n - 1;
    	int s = 0;
    	while (!(d & 1))
    		s++, d >>= 1;
    	for (int t = 0; t < T; t++) {
    		ll b = randLL() % n;
    		if (!b)
    			continue;
    		ll tmp = qpow(b, d, n);
    		if (tmp == 1 || tmp == n - 1)
    			continue;
    		for (int i = 0; i < s; i++) {
    			tmp = mul(tmp, tmp, n);
    			if (tmp == n - 1)
    				goto nextTurn;
    			if (tmp == 1 || tmp == 0)
    				return false;
    		}
    		if (tmp != 1)
    			return false;
    nextTurn:;
    	}
    	return true;
    }
    
    ll pollard_rho(ll x) {
    //	cerr << x << '
    ';
    	ll a, b, c, g;
    	if (!(x & 1))
    		return 2;
    	while (true) {
    		b = a = randLL() % x;
    		c = randLL() % 127 % x;
    		do {
    			a = add(mul(a, a, x), c, x);
    			b = add(mul(b, b, x), c, x);
    			b = add(mul(b, b, x), c, x);
    			g = gcd(b - a, x);
    			(g < 0) ? (g = -g) : (0);
    			if (g == x)
    				break;
    			if (g > 1)
    				return g;
    		} while (a != b);
    	}
    	assert(false);
    	return 0;
    }
    
    void get_primary_factors(ll x, vector<ll>& rt) {
    	if (x == 1) {
    		return;
    	}
    	if (miller_rabin(x)) {
    		rt.push_back(x);
    		return;
    	}
    	ll a = pollard_rho(x);
    	get_primary_factors(a, rt);
    	get_primary_factors(x / a, rt);
    }
    
    vector< pair<ll, int> > get_primary_factor(vector<ll>& vec) {
    	vector< pair<ll, int> > rt;
    	if (vec.empty())
    		return rt;
    	sort(vec.begin(), vec.end());
    	vector<ll>::iterator it = vec.begin();
    	rt.push_back(make_pair(*it, 1));
    	for (it = it + 1 ; it != vec.end(); it++)
    		if (*it == rt.back().first)
    			rt.back().second++;
    		else
    			rt.push_back(make_pair(*it, 1));
    	return rt;
    }
    
    /// Template ends
    
    typedef vector<pair<ll, int>> factor;
    
    factor get_factor(ll m) {
    	vector<ll> tmp;
    	get_primary_factors(m, tmp);
    	return get_primary_factor(tmp);
    }
    
    int T;
    ll m, phim;
    factor fac;
    
    int main() {
    	scanf("%lld%d", &m, &T);
    	fac = get_factor(m);
    	fac = get_factor(phim = m / fac[0].first * (fac[0].first - 1));
    	ll a, b, d;
    	while (T--) {
    		scanf("%lld%lld", &a, &b);
    		d = phim;
    		for (auto par : fac) {
    			while (!(d % par.first) && qpow(a, d / par.first, m) == 1)
    				d /= par.first;
    		}
    		if (qpow(b, d, m) == 1) {
    			puts("Yes");
    		} else {
    			puts("No");
    		}
    	}
    	return 0;
    }
  • 相关阅读:
    闰年测试
    EditBox的测试用例设计
    测试工程中的评审
    测试框架
    github
    第一次上机实验
    对软件测试的初步认识
    白盒测试
    Date : 日期对象
    C++ 格式化输出 及 输入 流
  • 原文地址:https://www.cnblogs.com/yyf0309/p/11808755.html
Copyright © 2011-2022 走看看