您的位置:首页 > 其它

【poj3358】消因子+BSGS 或 消因子+欧拉定理 两种方法

2016-06-23 17:21 597 查看
题意:给你一个分数,求它在二进制下的循环节的长度,还有第一个循环节从哪一位开始。

For example, x = 1/10 = 0.0001100110011(00110011)w and 0001100110011 is a preperiod and 00110011 is a period of 1/10.

思路一:


我们可以观察一下1/10这组数据,按照二进制转换法(乘二法),我们可以得到:
1/10  2/10 4/10 8/10 16/10 32/10 ...
然后都分子都尽可能减去10,得到:
1/10  2/10 4/10 8/10 6/10 2/10 ...
这时候,发现出现了重复,那么这个重复就是我们要求的最小循环。
抽象出模型如下:对p/q
首先p'=p/gcd(p,q)
q'=q/gcd(p,q);

然后我们就是求p'*2^i == p'*2^j (mod q')   (“==”表示同余,i<j)
经过变换得到:
p'*2^i*(2^(j-i)-1) ==0 (mod q')
也就是 q' | p'*2^i*(2^(j-i)-1)
由于gcd(p',q')=1,
得到: q' | 2^i*(2^(j-i)-1)
因为2^(j-i)-1为奇数,所以q'有多少个2的幂,i就是多少,而且i就是循环开始位置的前一位。
那么令q''为q'除去2的幂之后的数
此时 q'' | 2^(j-i)-1
也就是求出x,使得 2^x ==1 (mod q'')



就是求p*(2^i) == p*(2^j) (mod q),除过来就是2^(i-j)==1(mod q)

思路二:转自http://www.cnblogs.com/Konjakmoyu/p/5183339.html



实现方法:

方法一: 直接BSGS

求2^x==1(mod n),就先消因子,然后在BSGS求解。



过程中如果b%d!=0,那就说明在x>=T时无解。

方法二:欧拉定理

先消因子,则2与n互质。

求2^x==1(mod n),根据欧拉定理2^phi(n)==1(%n),然后找phi(n)的质因子k。

从小到大枚举质因子k,判断2^k==1(%n)则的得到答案。

代码

BSGS的:

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;

typedef long long LL;
const LL N=40000;
LL pl,bl;
LL p
;
bool vis
;
struct node{
LL d,id;
}bit
;

bool cmp(node x,node y){
if(x.d==y.d) return x.id<y.id;
return x.d<y.d;
}

LL exgcd(LL a,LL b,LL &x,LL &y)
{
if(b==0) {x=1,y=0;return a;}
LL tx,ty;
LL d=exgcd(b,a%b,tx,ty);
x=ty;y=tx-(a/b)*ty;
return d;
}

LL find(LL x)
{
int l=1,r=bl;
while(l<=r)
{
int mid=(l+r)>>1;
if(bit[mid].d==x) return bit[mid].id;
if(bit[mid].d<x) l=mid+1;
if(bit[mid].d>x) r=mid-1;
}
return -1;
}

void exBSGS(LL b,LL &xx,LL &yy)
{
LL t,m,g,x,y,pm,am;
while(b%2==0) {b/=2;xx++;}
t=2;
for(int i=1;i<=100;i++)
{
if(t%b==1) {yy=i;return ;}
t=t*2%b;
}
m=(LL)(ceil((double)sqrt((double)b)));
pm=1%b;bit[0].d=1%b;bit[0].id=0;
for(int i=1;i<=m;i++)
{
bit[i].d=bit[i-1].d*2%b;
bit[i].id=i;
pm=pm*2%b;
}
sort(bit+1,bit+1+m,cmp);
bl=1;
for(int i=2;i<=m;i++)
{
if(bit[i].d!=bit[bl].d) bit[++bl]=bit[i];
}
exgcd(pm,b,x,y);
am=x%b+b;
t=1%b;
for(int i=0;i<=m;i++)
{
x=find(t);
if(x!=-1) {yy=i*m+x;return ;}
t=t*am%b;
}
return ;
}

int main()
{
freopen("a.in","r",stdin);
freopen("b.out","w",stdout);
LL T=0,a,b;
char c;
while(scanf("%I64d%c%I64d",&a,&c,&b)!=EOF)
{
LL x,y;
LL g=exgcd(a,b,x,y);
a/=g,b/=g;
x=1,y=-1;
exBSGS(b,x,y);
printf("Case #%I64d: %I64d,%I64d \n",++T,x,y);
}
return 0;
}


欧拉定理的:

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;

typedef long long LL;
const LL Max=(LL)1e6;
const LL N=Max+100;
LL fl;
LL f
;

LL gcd(LL a,LL b)
{
if(b==0) return a;
return gcd(b,a%b);
}

bool cmp(int x,int y){return x<y;}

LL eular(LL x)
{
LL ans=x;
for(int i=2;i*i<=x;i++)
{
if(x%i==0) ans/=i,ans=ans*(i-1);
while(x%i==0) x/=i;
}
if(x>1) ans/=x,ans=ans*(x-1);
return ans;
}

LL quickpow(LL a,LL b,LL mod)
{
LL ans=1%mod;
while(b)
{
if(b&1) ans=ans*a%mod;
a=a*a%mod;
b>>=1;
}
return ans;
}

void solve(LL b,LL &xx,LL &yy)
{
LL k=2,x=b,ans=x;
while(b%2==0) xx++,b/=2;
LL phi=eular(b);
for(int i=1;i*i<=phi;i++)
{
if(phi%i==0) f[++fl]=i,f[++fl]=phi/i;
}
sort(f+1,f+1+fl,cmp);
for(int i=1;i<=fl;i++)
{
if(quickpow(2,f[i],b)==1) {yy=f[i];return ;}
}
}

int main()
{
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
LL T=0,a,b;
char c;
while(scanf("%I64d%c%I64d",&a,&c,&b)!=EOF)
{
LL x=1,y=0;
LL g=gcd(a,b);
a/=g,b/=g;
solve(b,x,y);
printf("Case #%I64d: %I64d,%I64d \n",++T,x,y);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: