hdu 5377 Root 原根+离散对数+扩展欧几里得
来源:互联网 发布:mac文件归类 编辑:程序博客网 时间:2024/06/05 07:04
题意:给定一个数字sum,有 m 个询问:(xi, yi),求最小的非负整数 ki 满足 xi^ki =yi (mod p)。
其中 p 是 sum 的质因子。
1<=sum<=10^8,1<=m<=10^5,0<=xi, yi<=10^9
分析:
直接用离散对数求解K的复杂度为O( m log m ),m为根号n。对于该题每次询问都要单独计算,复杂度太高。
需要用到原根,离散对数,扩展欧几里得计算。
整体思路:
对sum分解质因数, 对于x^k mod p = y ,
先求出p的原根d,对同余式两边取对数得到 k*logd(x) mod p-1 = logd(y)
再使用扩展欧几里德求出上式中的k
复杂度优化!!:
令:A=logd(x) B=logd(y)
对同余式取对数时:即 求d^A mod p = x d^B mod p = y
baby step中要对d^m打表,由于对于同一质因子p,d是相同的。所以可以提前初始化表。
那么总复杂度变为O(m+p/m*q),p为质数大小,m为表长,q为查询次数
其中查询次数 q 为10^5,所以表要足够大,以使p/m*q这一项较小。
#include <cstdio>#include <iostream>#include <algorithm>#include <cstring>#include <string>#include <cmath>#include <vector>#include <map>#include <tr1/unordered_map>#define clr(x, y) memset(x, y, sizeof x)using namespace std;typedef long long LL;const double eps=1e-8;const int maxn=100100;const int mod=1e6+10;LL prime[maxn];LL X[maxn],Y[maxn];LL A[maxn],B[maxn];LL ans[maxn];LL M;//质因数分解LL get_prime(LL n){ LL cnt=0; for(LL i=2;i*i<=n;i++) { if(n%i==0) { prime[cnt++]=i; while(n%i==0) n/=i; } } if(n!=1) prime[cnt++]=n; return cnt;}//快速幂LL pow_mod(LL a,LL p,LL n){ LL ans=1; while(p) { if(p&1) ans=(ans*a)%n; p>>=1; a=(a*a)%n; } return ans;}//求原根vector<LL>a;bool g_test(LL g,LL p){ for(LL i=0;i<a.size();i++) { if(pow_mod(g,(p-1)/a[i],p)==1) return 0; } return 1;}LL primitive_root(LL p){ LL tmp=p-1; a.clear(); for(LL i=2;i<=tmp/i;i++) { if(tmp%i==0) { a.push_back(i); while(tmp%i==0) tmp/=i; } } if(tmp!=1) a.push_back(tmp); LL g=1; while(true) { if(g_test(g,p)) return g; g++; }}//扩展欧几里得void gcd(LL a,LL b,LL &d,LL &x,LL &y){ if(!b) { d=a; x=1; y=0;} else { gcd(b,a%b,d,y,x); y-=x*(a/b); }}//求逆元LL inv(LL a,LL n){ LL d,x,y; gcd(a,n,d,x,y); return d==1 ? (x+n)%n : -1;}//手动哈希struct HashTable{ int top,head[mod]; struct Node { int x,y,next; }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) { for(int tx=head[x%mod];tx;tx=node[tx].next) { if(node[tx].x==x) return node[tx].y; } return -1; }}hs;//求出所有查询中X,Y关于a(mod n)的离散对数void log_mod(LL a,LL n){ LL m,v,e=1; m=mod; v=inv(pow_mod(a,m,n),n); //打表 a^0 ~ a^m-1, m为表长 hs.init(); for(int i=0;i<m;i++) { hs.insert(e,i); e=e*a%n; } //求出所有查询中X,Y关于a的离散对数 for(int j=0;j<M;j++) { //必须要取模后再计算 LL b=X[j]%n; for(int i=0;i<m;i++) { LL s=hs.query(b); if(s!=-1) { A[j]=i*m+s; break; } b=b*v%n; } b=Y[j]%n; for(int i=0;i<=n/m;i++) { LL s=hs.query(b); if(s!=-1) { B[j]=i*m+s; break; } b=b*v%n; } }}//用扩展欧几里得求a*x+b*y=c中x的最小非负整数解LL get_K(LL a,LL b,LL c){ LL d,x,y; gcd(a,b,d,x,y); if(c%d!=0) return -1; x=x*c/d; b=b/d; x=x-(x/b*b); if(x<0) x+=b; return x;}int main(){ int T; LL sum; scanf("%d",&T); for(int t=1;t<=T;t++) { memset(ans,-1,sizeof(ans)); scanf("%I64d %I64d",&sum,&M); printf("Case #%d:\n",t); for(int i=0;i<M;i++) { scanf("%I64d %I64d",&X[i],&Y[i]); } //质因数分解 LL cnt=get_prime(sum); LL temp; //枚举所有质因子 for(int j=0;j<cnt;j++) { //求该质因子的原根d LL d=primitive_root(prime[j]); //求所有的查询中X,Y关于d(mod prime[j])的离散对数 log_mod(d,prime[j]); for(int i=0;i<M;i++) { //求解最小的K, K*A[i]=B[i](mod (prime[j]-1)) temp=get_K(A[i],prime[j]-1,B[i]); if(temp!=-1) { if(ans[i]==-1) ans[i]=temp; else ans[i]=min(temp,ans[i]); } } } for(int i=0;i<M;i++) printf("%I64d\n",ans[i]); } return 0;}
0 0
- hdu 5377 Root 原根+离散对数+扩展欧几里得
- sgu261:Discrete Roots(原根+离散对数+扩展欧几里得)
- SGU261 一些数学知识入门与应用结合 原根 快速幂 离散对数 扩展欧几里得 单边元模线性方程
- Root(hdu5777+扩展欧几里得+原根)
- (离散对数与原根)
- 原根与离散对数
- 离散对数和原根
- BSGS+原根+离散对数
- hdu 3930(BSGS+原根+扩展欧几里得)
- HDU 2815 离散对数
- [BZOJ]2219 数论之神 离散对数 + 原根
- 模板,扩展,离散对数,数论
- HDU 2815 Mod Tree【扩展Baby Step Giant Step解决离散对数问题】
- HDU 2815 Mod Tree 离散对数 扩展Baby Step Giant Step算法
- x^A=B(mod C)的解 (离散对数与原根)
- 高次同余笔记(三):离散对数和原根
- hdu 2669 扩展欧几里得
- hdu 1576 扩展欧几里得
- 设计模式-原型模式
- HDU5377 Root
- c++中的 extern "C"
- 杭电1285确定比赛名次
- spring InitializingBean接口
- hdu 5377 Root 原根+离散对数+扩展欧几里得
- 好的资源项目
- Android签名打包后应用HOME键重启
- 黑马程序员——26,基本数据操作流,字节数组操作流,转换流,编码表
- Spring MVC 教程,快速入门,深入分析
- android 追踪手势移动
- android 判断手机是否支持前置摄像头
- delphi 数组 for循环
- mysql workbench 水平滚动条不见的办法