HDU5377 Root

来源:互联网 发布:mac文件归类 编辑:程序博客网 时间:2024/05/16 05:02

这个题我找不到别人写的博客大哭大哭大哭。。。。

题解:

对于x^k mod p = y

先求出p的原根d,对同余式两边取对数得到

k*logd(x) mod( p-1) = logd(y) (该定理可以在数论的书中找到详细证明)

再使用扩展欧几里德求出上式中的k 那么如何,对一个同余式取对数?

即求d^kx mod p = x(题解是这样讲的但是我不懂)

d^ky mod p = y

熟悉baby step的同学马上就会发现因为d是相同的,所以可以提前初始化表,那么总复杂度变为O(m+p/m*q),p为质数大小,m为表长,q为查询次数

那么至于原题中sum,我们直接分解质因数就行


按照我看标程的理解,k*logd(x) mod( p-1) = logd(y) ,令s=logd(x),t=logd(y),

即ks%(p-1)==t.

 所以有ks+m(p-1)==t ,由欧几里得扩展定理求出k和m ,kks+m(p-1)=gcd(s,p-1)=D,t必须为gcd(s,p-1)的倍数才有解。

式子两边乘以t/D恢复原式 ,kk*s*t/D+m(p-1)*t/D==D*t/D

所以最后结果就是k=kk*t/D.

求一个质数p的原根的一个简便方法:求出p-1(注意是p-1)所有不同的质因子p1,p2...pm,对于任何2<=a<=x-1,判定a是否为x的原根,只需要检验
a^((x-1)/p1),a^((x-1)/p2),...a^((x-1)/pm)这m个数中,是否存在一个数mod x为1,若存在,a不是x的原根,否则就是x的原根

他是求出sum的全部质因子,每求出一个质因子求出该质因子的最小原根,然后求出对应的k,再找最小的k

#include<iostream>#pragma comment(linker,"/STACK:1024000000,1024000000")#include<cstring>#include<string>#include<cmath>#include<cstdio>#include<queue>#include<vector>#include<map>#include<algorithm>#include<set>#define INF 0x3f3f3f3fusing namespace std;typedef long long ll;const int maxn = 1e6 + 1;const int maxv = 1e5 + 1;bool isnp[maxv];int prime[maxv], pnum;//素数总个数int cas;void get_prime()//素数筛{pnum = 0;int i, j;memset(isnp, 0, sizeof(isnp));isnp[0] = isnp[1] = true;for (i = 2; i<maxv; i++){if (!isnp[i])prime[pnum++] = i;for (j = 0; j<pnum&&prime[j] * i<maxv; j++){isnp[i*prime[j]] = true;if (i%prime[j] == 0)break;}}}ll qukpow(ll k, ll base, ll p)//快速幂 base^k%p{ll res = 1;for (; k; k >>= 1){if (k & 1)res = (res*base) % p;base = (base*base) % p;}return res;}ll fpr[maxv];//存储p-1的质因子ll find_primitive_root(ll p)//求出p-1所有质因子,放入fpr,返回p最小原根{ll cnt = 0, num = p - 1, res;int i;if (p == 2)return 1;for (i = 0; i<pnum&&prime[i] * prime[i] <= num&&num>1; i++)//求出p-1所有不同的质因子{if (num%prime[i] == 0){fpr[cnt++] = prime[i];while (num%prime[i] == 0)num /= prime[i];}}if (num>1)fpr[cnt++] = num;/*求出p-1所有不同的质因子p1,p2...pm,对于任何2<=a<=x-1,判定a是否为x的原根,只需要检验a^((x-1)/p1),a^((x-1)/p2),...a^((x-1)/pm)这m个数中,是否存在一个数mod x为1,若存在,a不是x的原根,否则就是x的原根。*/for (res = 2; res <= p - 1; res++)//枚举原根{for (i = 0; i<cnt; i++)if (qukpow(p / prime[i], res, p) == 1)break;if (i >= cnt)return res;}return -1;};const int mod = 1e6 + 7;struct solve{struct HashTable{int top, head[mod];struct Node{int x, y, next;//x是原根p的幂,y是幂次数,next是y+1次幂所在的head桶}node[mod];void init(){top = 0;memset(head, 0, sizeof(head));}void insert(ll x, ll y){node[top].x = x; node[top].y = y; node[top].next = head[x%mod];head[x%mod] = top++;}ll query(ll x)//求出x是原根proot的几次幂{for (int tx = head[x%mod]; tx; tx = node[tx].next)if (node[tx].x == x)return node[tx].y;return -1;}}mp;ll p;//sum的质因子ll discretelog(ll prt, ll a)//求log prt(a),a是prt的几次幂{ll res, am = qukpow(maxn - 1, prt, p), inv = qukpow(p - 2, a, p), x = 1;for (ll i = maxn - 1;; i += (maxn - 1)){if ((res = mp.query((x = x*am%p)*inv%p)) != -1){return i - res;}if (i>p)break;}return -1;}//扩展欧几里德  ax+by=dvoid ex_gcd(long long a, long long b, long long &d, long long &x, long long &y){if (!b){ d = a; x = 1; y = 0; }else { ex_gcd(b, a%b, d, y, x); y -= x*(a / b); }}ll proot;//质因子p的原根void init(){mp.init();ll tmp, x, y, d;int i;proot = find_primitive_root(p);//proot 质因子p的原根for (i = 0, tmp = 1; i<maxn - 1; i++, tmp = tmp*proot%p)mp.insert(tmp%p, i * 1ll);//原根p的幂,幂次数}ll query(ll x, ll y){ll d;x %= p;y %= p;if (y == 1)return 0; //x^k==1,k=0else if (x == 0){if (y == 0)return 1;//0^k==0,k=1;else return -1;//0^k==-1,k无解}else if (y == 0)return -1; //x^k==0,x!=0,k无解else{ll s = discretelog(proot, x);//令s=logd(x),ll t = discretelog(proot, y);//t=logd(y),ex_gcd(s, p - 1, d, x, y);//由欧几里得扩展定理求出k和m ,kks+m(p-1)=gcd(s,p-1)=Dif (t%d)return -1;//t必须为gcd(s,p-1)的倍数才有解else{//式子两边乘以t/D恢复原式 ,kk*s*t/D+m(p-1)*t/D==D*t/D//所以最后结果就是k = kk*t / D.ll dx = (p - 1) / d;x = (x%dx + dx) % dx;x *= (t / d);x = (x%dx + dx) % dx;return x;}}}}sol[32];//sol[i]表示sum第i个质因子,求出其对应的原根,并检验int main(){int i, j, q, con, T;ll sum, x, y;scanf("%d", &T);get_prime();for (cas = 1; cas <= T; cas++){con = 0;//sum质因子个数scanf("%I64d %d", &sum, &q);for (i = 0; i<pnum&&prime[i] * prime[i] <= sum&&sum != 1; i++){if (sum%prime[i] == 0){sol[con].p = prime[i];//筛出sum的质因子sol[con].init();con++;while (sum%prime[i] == 0)sum /= prime[i];}}if (sum>1){sol[con].p = sum;sol[con].init();con++;}printf("Case #%d:\n", cas);for (i = 0; i<q; i++){scanf("%lld %lld", &x, &y);ll res = 1e18, tmp;for (j = 0; j<con; j++){tmp = sol[j].query(x, y);if (tmp != -1)res = min(res, tmp);}if (res == 1e18)res = -1;printf("%I64d\n", res);}}return 0;}

余式两边取对数得到

k*logd(x) mod( p-1) = logd(y) (该定理可以在数论的书中找到详细证明)

再使用扩展欧几里德求出上式中的k 那么如何,对一个同余式取对数?

即求d^kx mod p = x(题解是这样讲的但是我不懂)

d^ky mod p = y

熟悉baby step的同学马上就会发现因为d是相同的,所以可以提前初始化表,那么总复杂度变为O(m+p/m*q),p为质数大小,m为表长,q为查询次数

那么至于原题中sum,我们直接分解质因数就行


按照我看标程的理解,k*logd(x) mod( p-1) = logd(y) ,令s=logd(x),t=logd(y),

即ks%(p-1)==t.

 所以有ks+m(p-1)==t ,由欧几里得扩展定理求出k和m ,kks+m(p-1)=gcd(s,p-1)=D,t必须为gcd(s,p-1)的倍数才有解。

式子两边乘以t/D恢复原式 ,kk*s*t/D+m(p-1)*t/D==D*t/D

所以最后结果就是k=kk*t/D.

求一个质数p的原根的一个简便方法:求出p-1(注意是p-1)所有不同的质因子p1,p2...pm,对于任何2<=a<=x-1,判定a是否为x的原根,只需要检验
a^((x-1)/p1),a^((x-1)/p2),...a^((x-1)/pm)这m个数中,是否存在一个数mod x为1,若存在,a不是x的原根,否则就是x的原根

他是求出sum的全部质因子,每求出一个质因子求出该质因子的最小原根,然后求出对应的k,再找最小的k


0 0
原创粉丝点击