zoukankan      html  css  js  c++  java
  • 【ybt金牌导航8-6-6】【HDU 3930】N次剩余(模数为素数)/ Broot

    N次剩余(模数为素数)/ Broot

    题目链接:ybt金牌导航8-6-6 / HDU 3930

    题目大意

    求 x^k≡newx 在模素数 m 意义下的解 x。

    思路

    先把式子弄好康点:(x^k≡newx(mod m))

    有一个东西比较显然,就是 (x) 是模 (m) 意义下的剩余类,那自然会 (0leq x<m)

    首先,我们先求 (m) 的原根 (root),因为 (m) 是素数,所以我们可以把 ({1,2,...,m-1})({root^1,root^2,...,root^{m-1}}) 建立一一对应的关系。

    那好,我们自然会得出两个东西 (root) 的次方来表示 (k,newx)
    (root^{y}≡x(mod m),root^{t}≡newx(mod m)),那带进去式子:
    (root^{y imes k}≡root^{t}(mod m))
    (m) 是质数,两边就不可以是 (0)(根据前面对应的式子也可以看出),那根据一一对应关系,就可以得到这个:(因为是 (m-1) 对数一一对应,所以模数是 ((m-1))
    (y imes k≡t(mod (m-1)))

    那我们分别来看看这些式子要怎么解:
    (y imes k≡t(mod (m-1))) 这个式子就是一个同余方程,扩展欧几里得直接带走。
    然后还要求出怎么一一对应:(root^{k}≡newx(mod m)),我们要求出 (k)
    自然看到这个可以用 BSGS 来做,而且因为 (m) 是质数,所以不同扩展。

    然后我们已经求出了每个 (y),我们就带回去 (root^y≡x(mod m)) 中,就可以得到这个 (y) 对应的 (x) 了。

    总的来说,步骤就这几个:

    1. (m) 的原根 (root)
    2. BSGS 求 (newx) 对应 (root)(t) 次方的这个 (t)
    3. (y imes k≡t(mod (m-1))) 这个同余方程,先算最小解
    4. 把每个解都求出来,对于每个解还原回对于的 (x)
    5. 把所有得出的 (x) 排序,输出

    (总的来说就是非常麻烦,非常恶心)

    代码

    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define ll long long
    #define hsh_mo 999979
    
    using namespace std;
    
    ll k, m, newx, ans[10000001];
    
    ll tot_time, zyz[10000001], prime[1000001];
    bool np[10000001], yes;
    
    struct node {
    	ll to, x;
    	int nxt;
    }e[10000001];
    int hsh[1000001];
    ll KK;
    
    void get_prime() {//欧拉筛筛出素数
    	for (int i = 2; i <= 10000000; i++) {
    		if (!np[i]) {
    			prime[++prime[0]] = i;
    		}
    		for (int j = 1; j <= prime[0] && 1ll * i * prime[j] <= 10000000ll; j++) {
    			np[i * prime[j]] = 1;
    			if (i % prime[j] == 0) break;
    		}
    	}
    }
    
    
    void get_zyz(ll x) {//分解质因子
    
    	zyz[0] = 0;
    	for (int i = 1; prime[i] * prime[i] <= x; i++) {
    		if (x % prime[i] == 0) {
    			zyz[++zyz[0]] = prime[i];
    			while (x % prime[i] == 0) x /= prime[i];
    		}
    	}
    	if (x > 1) zyz[++zyz[0]] = x;
    }
    
    //不能直接乘是因为直接乘会爆 long long
    /*
    //龟速乘 
    ll times_mo(ll x, ll y, ll mo) {
    	ll re = 0;
    	while (y) {
    		if (y & 1) re = (re + x) % mo;
    		x = (x + x) % mo;
    		y >>= 1;
    	}
    	return re;
    }
    */
    
    //并不龟速的龟速黑科技乘(利用了 long double)
    ll times_mo(ll x, ll y, ll mo) {
    	x %= mo;
    	y %= mo;
    	ll c = (long double)x * y / mo;
    	long double re = x * y - c * mo;
    	if (re < 0) re += mo;
    		else if (re >= mo) re -= mo;
    	return re;
    }
    
    ll ksm_mo(ll x, ll y, ll mo) {//快速幂
    	ll re = 1;
    	x %= mo;
    	while (y) {
    		if (y & 1) re = times_mo(re, x, mo);
    		x = times_mo(x, x, mo);
    		y >>= 1;
    	}
    	return re;
    } 
    
    ll get_first_root(ll x) {//求最小原根
    	get_zyz(x - 1);
    	
    	for (ll i = 1; ; i++) {
    		yes = 1;
    		for (int j = 1; j <= zyz[0]; j++)
    			if (ksm_mo(i, (x - 1) / zyz[j], x) == 1) {
    				yes = 0;
    				break;
    			}
    		if (yes) return i; 
    	}
    }
    
    void csh_BSGS() {
    	memset(hsh, 0, sizeof(hsh));
    	KK = 0;
    }
    
    void hsh_push(ll x, ll id) {//BSGS里面哈希的插入
    	ll pl = x % hsh_mo;
    	for (int i = hsh[pl]; i; i = e[i].nxt)
    		if (e[i].to == x) {
    			return ;
    		}
    	
    	KK++;
    	e[KK].to = x;
    	e[KK].nxt = hsh[pl];
    	e[KK].x = id;
    	hsh[pl] = KK;
    }
    
    ll hsh_ask(ll x) {//BSGS里面哈希的询问
    	ll pl = x % hsh_mo;
    	for (int i = hsh[pl]; i; i = e[i].nxt)
    		if (e[i].to == x) return e[i].x;
    	return -1;
    }
    
    //g^t=a(mod mo) (无需扩展
    //这里与我之前打的 BSGS 不同,但只是将式子化成了不同的形式
    //(其实是因为我之间的做法好像会有锅)
    ll BSGS(ll g, ll a, ll mo) {
    	csh_BSGS();
    	
    	int n = sqrt(mo) + 1;
    	
    	ll now = 1;
    	for (int i = 0; i < n; i++) {
    		hsh_push(now, i); 
    		now = times_mo(now, g, mo);
    	}
    	
    	int ntimes = ksm_mo(g, (mo - n - 1 + mo) % mo, mo);
    	now = a;
    	for (int i = 0; i < n; i++) {
    		ll mz = hsh_ask(now);
    		if (mz != -1) return i * n + mz;
    		now = times_mo(now, ntimes, mo);
    	}
    	
    	return -1;
    }
    
    
    void csh_work() {
    	ans[0] = 0;
    }
    
    ll gcd(ll x, ll y) {//求 gcd
    	if (!y) return x;
    	return gcd(y, x % y);
    }
    
    ll exgcd(ll a, ll b, ll &x, ll &y) {//求扩展欧几里得
    	if (!b) {
    		x = 1;
    		y = 0;
    		return a;
    	}
    	ll d = exgcd(b, a % b, y, x);
    	y -= (a / b) * x;
    	return d;
    }
    
    void write() {//输出
    	if (ans[0] == 0) {
    		printf("-1
    ");
    		return ;
    	}
    	for (int i = 1; i <= ans[0]; i++) {
    		printf("%lld
    ", ans[i]);
    	}
    }
    
    void work(ll k, ll m, ll newx) {
    	csh_work();
    	
    	ll root = get_first_root(m) % m;//第一步
    	ll t = BSGS(root, newx, m);//第二步
    	
    	// xk=t(mod tmod) => k*x+tmod*y=t
    	//解上面的这个式子
    	ll tmod = m - 1;
    	ll x, y;
    	ll d = exgcd(k, tmod, x, y);
    	if (t % d) {
    		write();
    		return ;
    	}
    	
    	//根据第一个答案算出所有的答案
    	x = x * (t / d) % tmod;
    	ll step = tmod / d;
    	for (int i = 0; i < d; i++) {
    		x = ((x + step) % tmod + tmod) % tmod;
    		ans[++ans[0]] = ksm_mo(root, x, m);
    	}
    	sort(ans + 1, ans + ans[0] + 1);
    	
    	write();
    }
    
    //x^k=newx(mod m)
    int main() {
    	get_prime();
    	
    	while (scanf("%lld %lld %lld", &k, &m, &newx) != EOF) {
    		printf("case%lld:
    ", ++tot_time);
    		work(k, m, newx);
    	}
    	
    	return 0;
    }
    
  • 相关阅读:
    Springboot使用PlatformTransactionManager接口的事务处理
    js 正则替换html标签
    【转】mysql查询时,查询结果按where in数组排序
    js输出字幕数字a-zA-Z0-9
    tcpdump使用教程
    rsync安装使用教程
    vim配置修改教程
    XD刷机报错bad CRC
    使用docker搭建seafile服务器
    案例:使用sqlplus登录报ORA-12547错误
  • 原文地址:https://www.cnblogs.com/Sakura-TJH/p/YBT_JPDH_8-6-6.html
Copyright © 2011-2022 走看看