[SDOI2009]SuperGCD 【压位高精度+辗转相减】
题目链接:
https://www.luogu.org/problemnew/show/P2152
题目概述:
计算两个大整数(A,B)的最大公因数
数据范围
0 < A , B ≤ 10 ^
其一在于辗转相减法——辗转相除法的优化(针对大数,避免了大数的模运算带来的多方面的复杂度)
思想就是 以数次 A-B 代替 A%B (这二者是等价的)
辗转相减法:
1 /* 2 Write(X) 输出X 3 Down(X) X除以2 4 Up(X) X乘以2 5 */ 6 void Solve(){ 7 int t=0; 8 int ok=0; 9 while(A!=B){ 10 if(A<B) swap(A,B); 11 int x=A.number[1]%2,y=B.number[1]%2; 12 if(x==0 && y==0){ 13 t++; 14 Down(A),Down(B); 15 } 16 else if(x==0) Down(A); 17 else if(y==0) Down(B); 18 else A=A-B; 19 } 20 while(t--) Up(A); 21 Write(A); 22 return ; 23 }
其二就在于压位高精度本身的实现了,我这里选用的是压四位(例如Number[1]=0023,Number[2]=0034表示340023)
结构体(包括 小于比较;不等于比较;减法)
1 #include<map> 2 #include<queue> 3 #include<cmath> 4 #include<cstdio> 5 #include<cstring> 6 #include<iostream> 7 #include<algorithm> 8 using namespace std; 9 typedef long long ll; 10 const int Hash=100003; 11 int A,B,N; 12 int Tot=0; 13 int F[Hash+5]; 14 struct data{ 15 ll to; 16 int next; 17 ll cost; 18 }E[Hash+5]; 19 void Addl(int x,ll y,ll z){ 20 // printf("%d %d %d\n",x,y,z); 21 E[++Tot]=(data){y,F[x],z}; 22 F[x]=Tot; 23 return ; 24 } 25 int H(ll x){ 26 return abs(x%Hash); 27 } 28 int Updata(ll x,int y,ll z){ 29 int t=H(x); 30 int i=F[t]; 31 while(i){ 32 ll c=E[i].to; 33 if(c==x) return E[i].cost; 34 i=E[i].next; 35 } 36 if(y==0) return -1; 37 if(y==1) Addl(t,x,z); 38 return 1; 39 } 40 ll Exgcd(ll a,ll b,ll &x,ll &y){ 41 if(b==0){ 42 x=1; 43 y=0; 44 return a; 45 } 46 ll res=Exgcd(b,a%b,y,x); 47 y-=a/b*x; 48 return res; 49 } 50 //map<ll,int>mp; 51 void Solve(ll a,ll b,ll c){ 52 Tot=0; 53 memset(F,0,sizeof(F)); 54 a%=c; 55 ll t=1; 56 ll tmp=1; 57 ll m=sqrt(c); 58 if(m*m<N) m++; 59 Updata(1,1,m+1); 60 // mp.clear(); 61 // mp[1]=m+1; 62 for(ll i=1;i<m;i++){ 63 tmp=(tmp*a)%c; 64 Updata(tmp,1,i); 65 //if(!mp[tmp]) mp[tmp]=i; 66 } 67 tmp=tmp*a%c; 68 ll d=1,x,y; 69 for(ll i=0;i<m;i++){ 70 ll x,y; 71 ll res=Exgcd(d,c,x,y); 72 ll need=(b/res*x%c+c)%(c/res); 73 int t=Updata(need,0,-1); 74 // printf("%d %d<\n",need,t); 75 if(t>0){ 76 if(t==m+1) t=0; 77 cout<<i*m+t<<endl; 78 return ; 79 } 80 d=(d*tmp)%c; 81 } 82 printf("-1\n"); 83 return ; 84 } 85 int main(){ 86 while(scanf("%d%d%d",&A,&B,&N)!=EOF) 87 Solve((ll)A,(ll)B,(ll)N); 88 return 0; 89 }
乘2及除2
1 void Up(alpha &x){ 2 int now=1; 3 while(now<=x.large){ 4 x.number[now]*=2; 5 now++; 6 } 7 8 now=1; 9 while(now<=x.large){ 10 if(x.number[now]>=10000) x.number[now]-=10000,x.number[now+1]++; 11 now++; 12 } 13 14 while(x.number[x.large+1]) x.large++; 15 return ; 16 } 17 void Down(alpha &x){ 18 int now=x.large; 19 while(now){ 20 if(x.number[now]%2) x.number[now-1]+=10000; 21 x.number[now--]/=2; 22 } 23 while(x.large>1 && !x.number[x.large]) x.large--; 24 return ; 25 }
读入及输出
1 void Read(alpha &x){ 2 alpha a; 3 char c=getchar(); 4 while(!isdigit(c)) c=getchar(); 5 while(isdigit(c)) a.number[++a.large]=c-'0',c=getchar(); 6 x.large=(a.large-1)/4+1; 7 reverse(a.number+1,a.number+1+a.large); 8 while(a.large>1 && !a.number[a.large]) a.large--; 9 while(a.large) x.number[(a.large-1)/4+1]=x.number[(a.large-1)/4+1]*10+a.number[a.large--]; 10 return ; 11 } 12 void Write(alpha x){ 13 alpha a; 14 int front=1; 15 a.large=4*x.large; 16 while(front<=x.large){ 17 for(int i=1;i<=4;i++) 18 a.number[(front-1)*4+i]=x.number[front]%10,x.number[front]/=10; 19 front++; 20 } 21 while(a.large>1 && !a.number[a.large]) a.large--; 22 while(a.large) putchar(a.number[a.large--]+'0'); 23 putchar('\n'); 24 return ; 25 }
全code
1 /* 2 SuperGcd 3 LG 1414 4 */ 5 #include<queue> 6 #include<cstdio> 7 #include<cstring> 8 #include<iostream> 9 #include<algorithm> 10 using namespace std; 11 struct alpha{ 12 int large; 13 int number[10005]; 14 alpha(){ 15 large=0; 16 memset(number,0,sizeof(number)); 17 } 18 friend bool operator<(alpha a,alpha b){ 19 if(a.large!=b.large) return a.large<b.large; 20 while(a.large) 21 if(a.number[a.large]!=b.number[a.large--]) return a.number[++a.large]<b.number[a.large]; 22 return 0; 23 } 24 friend bool operator!=(alpha a,alpha b){ 25 if(a.large!=b.large) return 1; 26 while(a.large) 27 if(a.number[a.large]!=b.number[a.large--]) return 1; 28 return 0; 29 } 30 friend alpha operator-(alpha a,alpha b){ 31 alpha c; 32 while(c.large<a.large){ 33 c.number[++c.large]+=a.number[c.large]-b.number[c.large]; 34 while(c.number[c.large]<0) c.number[c.large]+=10000,c.number[c.large+1]--; 35 } 36 while(c.large>1 && !c.number[c.large]) c.large--; 37 return c; 38 } 39 }; 40 void Read(alpha &x){ 41 alpha a; 42 char c=getchar(); 43 while(!isdigit(c)) c=getchar(); 44 while(isdigit(c)) a.number[++a.large]=c-'0',c=getchar(); 45 x.large=(a.large-1)/4+1; 46 reverse(a.number+1,a.number+1+a.large); 47 while(a.large>1 && !a.number[a.large]) a.large--; 48 while(a.large) x.number[(a.large-1)/4+1]=x.number[(a.large-1)/4+1]*10+a.number[a.large--]; 49 return ; 50 } 51 void Write(alpha x){ 52 alpha a; 53 int front=1; 54 a.large=4*x.large; 55 while(front<=x.large){ 56 for(int i=1;i<=4;i++) 57 a.number[(front-1)*4+i]=x.number[front]%10,x.number[front]/=10; 58 front++; 59 } 60 while(a.large>1 && !a.number[a.large]) a.large--; 61 while(a.large) putchar(a.number[a.large--]+'0'); 62 putchar('\n'); 63 return ; 64 } 65 alpha A,B; 66 void Up(alpha &x){ 67 int now=1; 68 while(now<=x.large){ 69 x.number[now]*=2; 70 now++; 71 } 72 73 now=1; 74 while(now<=x.large){ 75 if(x.number[now]>=10000) x.number[now]-=10000,x.number[now+1]++; 76 now++; 77 } 78 79 while(x.number[x.large+1]) x.large++; 80 return ; 81 } 82 void Down(alpha &x){ 83 int now=x.large; 84 while(now){ 85 if(x.number[now]%2) x.number[now-1]+=10000; 86 x.number[now--]/=2; 87 } 88 while(x.large>1 && !x.number[x.large]) x.large--; 89 return ; 90 } 91 void Solve(){ 92 int t=0; 93 int ok=0; 94 while(A!=B){ 95 if(A<B) swap(A,B); 96 int x=A.number[1]%2,y=B.number[1]%2; 97 if(x==0 && y==0){ 98 t++; 99 Down(A),Down(B); 100 } 101 else if(x==0) Down(A); 102 else if(y==0) Down(B); 103 else A=A-B; 104 } 105 while(t--) Up(A); 106 Write(A); 107 return ; 108 } 109 int main(){ 110 Read(A);Read(B); 111 Solve(); 112 return 0; 113 }
对于我这样的蒟蒻来讲,这代码可真是难码。。。。。(不过还是很有成就感就是了)