数论问题精选

pku2689:

题目大意:给定区间[l,r],求[l,r]中距离最近的素数对和距离最远的素数对,如果存在tie,那么输出第一对。

解法:

      如果l和r都比较小的话,我们可以使用筛法,但是这个题目的l和r都非常大,所以直接的筛法会TLE并且MLE。注意到题目要求是r-l<=10^6,并且l<r<2,147,483,647。想一想筛法的过程——对于素数i,它开始筛的位置是i*i,小于i*i的位置是没有必要再去筛的,不是素数的肯定都被筛过了。所以对于l,r,我们只需要利用小于等于sqrt(r)的素数去筛[l,r]这个区间中的数即可。由于r-l<=10^6,可以开个长度为10^6的数组,0位置表示l,r-l位置表示r。

注意:

       此题实际数据貌似超过了int,需要使用long long。

View Code
#include <iostream>
#include
<cstdio>
#include
<cstring>
usingnamespace std;

typedef
longlong llg;

const llg N =1000010;
const llg M = N/10;

llg l, u, cnt, ct, p[M], tp[N
/2];
bool visit[N];

void initData()
{
llg i, j;
for(i =2; i < M; i++) visit[i] =true;
for(i =2; i < M; i++)
if(visit[i])
{
j
= i*i;
while(j < M)
{
if(visit[j]) visit[j] =false;
j
+= i;
}
}
cnt
=0;
for(i =2; i < M; i++)
if(visit[i])
p[cnt
++] = i;
}

int main()
{
llg i, j, pos, dis;
initData();
while(scanf("%I64d%I64d", &l, &u) != EOF)
{
if(l ==1) l++;
memset(visit,
true, sizeof(visit));
for(i =0; i < cnt; i++)
{
if(p[i]*p[i] > u) break;
j
= l/p[i]*p[i];
if(l%p[i] !=0) j += p[i];
if(j > u) continue;
if(j == p[i]) j += p[i];
j
-= l;
while(j <= u-l)
{
if(visit[j]) visit[j] =false;
j
+= p[i];
}
}
ct
=0;
for(i =0; i <= u-l; i++)
if(visit[i])
tp[ct
++] = l+i;
if(ct <=1)
{
printf(
"There are no adjacent primes.\n");
continue;
}
pos
= dis = N<<1;
for(i =1; i < ct; i++)
if(dis>tp[i]-tp[i-1])
{
dis
= tp[i] - tp[i-1];
pos
= i;
}
printf(
"%I64d,%I64d are closest, ", tp[pos-1], tp[pos]);
pos
= dis =-1;
for(i =1; i < ct; i++)
if(dis<tp[i]-tp[i-1])
{
dis
= tp[i] - tp[i-1];
pos
= i;
}
printf(
"%I64d,%I64d are most distant.\n", tp[pos-1], tp[pos]);
}
return0;
}

 

zoj2562:

题目大意:给定一个数n(n<=10^18),求小于n的且约数最多的一个数,如果存在tie,那么输出最小的一个。

解法:

      这个题和ural里的一个题比较像,具体哪个我忘了。。。这个题目说的更专业一点,应该是指的数论里的反素数。对于正整数x,g(x)表示x的约数的个数,如g(6)=4。如果正整数x满足,对于任意0<i<x,都有g(i)<g(x),则x为反素数。此题的本质即是求小于n的最大的反素数。

      首先,枚举的话肯定不行- -!不能枚举我们如何知道一个数是不是反素数呢?不用管他是不是什么反素数,最大的反素数肯定是小于n的那个约数最多的数。。。。说着说着又回来了。。。如果让我们去构造一个数,并且让它的约数尽量多,我们应该怎么做呢?首先,想一想算数基本定理,因为我们要找尽量小的那个数,所以假如2^1*5^1和2^1*3^1都满足条件的话,我们肯定会选择2^1*3^1,也就是说肯定是小素数因子优先;其次假如2^1*3^2和2^2*3^1都满足条件的话,我们肯定选择2^2*3^1,这也就是说素因子从小到大,他们的指数应该从大到小。更形式话一点:如果我们构造的数x=p1^k1*p2^k2*p3^k3*......*pm^km,那么要求的解必定满足: (1)p1到pm是最小的m个质数 (2)k1>=k2>=k3>=...>=km。然后直接dfs就可以了。

View Code
#include <iostream>
#include
<cstdio>
#include
<cstring>
usingnamespace std;

typedef
longlong llg;

const llg M = (llg)1000000000*1000000000;

llg n, cnt, ans, tot, p[
20];

void initData()
{
int i, j;
llg tmp
=1;
bool visit[50];
memset(visit,
true, sizeof(visit));
cnt
=0;
for(i =2; i <50; i++)
if(visit[i])
{
if(M/tmp < i) break;
p[cnt
++] = i;
j
= i*i;
while(j <50)
{
if(visit[j]) visit[j] =false;
j
+= i;
}
}
}

void dfs(int pre, int pos, llg sum, llg x)
{
int i;
llg tmp
=1;
if(sum>tot || (sum==tot && x<ans))
{
ans
= x;
tot
= sum;
}
if(pos == cnt) return;
for(i =1; i <= pre; i++)
{
tmp
*= p[pos];
if(n/tmp < x) break;
dfs(i, pos
+1, sum*(i+1), x*tmp);
}
}

int main()
{
initData();
while(scanf("%lld", &n) != EOF)
{
ans
= tot =-1;
dfs(
66, 0, 1, 1);
printf(
"%lld\n", ans);
}
return0;
}

 

pku2773:欧拉函数解法。

首先需要证明一下一个结论:如果gcd(a,m)=1,那么gcd(a+m, m)=1。

证明:用反证法

      假设gcd(a+m,m)=t>1,则设(a+m)=p*t,m=q*t,则(a+m)-m = p*t - q*t = (p-q)*t 因为a,m>0,所以p>q,所以a=(p-q)*t>=2。所以gcd(a,m)==t>=2,与已知相矛盾。所以gcd(a+m,m)=1。也就是说,可以利用互质的周期性来求解。

View Code
#include <iostream>
#include
<cstdio>
#include
<cstring>
usingnamespace std;

constint Max =300100000;

typedef
longlong llg;

llg n, k, ans;

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

int check(llg x)
{
llg t
=0, i;
t
+= x/n*ans;
for(i = x/n*n+1; i <= x; i++)
if(gcd(n, i)==1)
{
t
++;
if(t > k) return1;
}
if(t == k) return0;
return-1;
}

int main()
{
llg i, tmp, res;
while(scanf("%I64d%I64d", &n, &k) != EOF)
{
ans
= tmp = n;
if(n ==1)
{
printf(
"%I64d\n", k);
continue;
}
for(i =2; i*i <= tmp; i++)
if(tmp%i ==0)
{
ans
= ans/i*(i-1);
while(tmp%i ==0) tmp /= i;
}
if(tmp !=1) ans = ans/tmp*(tmp-1);
res
= k % ans;
if(res ==0)
{
k
-= ans;
res
= ans;
}
for(i = k/ans*n+1; res >0; i++)
{
if(!res) break;
if(gcd(i, n) ==1) res--;
}
printf(
"%I64d\n", i-1);
}
return0;
}

 

pku1284:

说实话,这个题我还真不会证明,数论学得太歇菜了。。以后若有空一定好好补补。。。

这个题牵扯到一个定理:如果模p乘法群有原根,那么原根的个数为phi(phi(p-1)),对于合数同样也成立。如果知道这个定理的话,那剩下的就很简单啦。

View Code

 

pku1181:

需要miller-rabin和pollard-rho,miller-rabin的原理倒是理解,不过pollard还不是很明白,先当模版了。。。这道题G++RE的很诡异啊,不知道为什么,把堆栈的深度控制在100也会RE,汗啊。。。但是C++却能AC,不知道两种编译器内在这有什么差异~。

还有就是,从这个题可以看出,取模运算确实是慢啊。。。看到某牛写的code,加了个常数优化可以砍掉500+ms~

View Code
#include <iostream>
#include
<ctime>
#include
<cstdio>
#include
<cstdlib>
#include
<cstring>
usingnamespace std;

typedef
longlong llg;

constint S =70;

llg tot, p[
80];

llg mullg(llg a, llg b, llg n)
{
llg ans
=0, tmp = a%n;
while(b)
{
if(b &1) ans += tmp;
if(ans >= n) ans -= n;
b
>>=1;
tmp
<<=1;
if(tmp >= n) tmp -= n;
}
return ans;
}

llg powMod(llg a, llg b, llg n)
{
llg ans
=1, tmp = a % n;
while(b)
{
if(b &1) ans = mullg(ans, tmp, n);
b
>>=1;
tmp
= mullg(tmp, tmp, n);
}
return ans;
}

bool witness(llg a, llg n)
{
int i, t =0;
llg x, tx, nn
= n-1;
while((nn&1) ==0)
{
nn
>>=1;
t
++;
}
x
= powMod(a, nn, n);
for(i =1; i <= t; i++)
{
tx
= mullg(x, x, n);
if(tx==1&& x!=1&& x!=n-1)
returntrue;
x
= tx;
}
if(x !=1) returntrue;
returnfalse;
}

bool millerRabin(llg n)
{
int i;
llg a;
if(n <2) returnfalse;
if(n ==2) returntrue;
for(i =1; i <= S; i++)
{
a
= (llg)rand()%(n-1) +1;
if(witness(a, n))
returnfalse;
}
returntrue;
}

llg Abs(llg x)
{
if(x <0) x =-x;
return x;
}

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

llg pollardRho(llg n,
int c)
{
int i =0;
llg x, y, k, d;
y
= x = rand()%(n-1) +1;
k
=2;
while(1)
{
i
++;
x
= (mullg(x, x, n)+c) % n;
d
= gcd(Abs(y-x), n);
if(d!=1&& d!=n) return d;
if(y == x) return n;
if(i == k)
{
y
= x;
k
<<=1;
}
}
}

void findFac(llg n)
{
if(millerRabin(n))
{
p[tot
++] = n;
return;
}
else
{
//if(depth > 100) return;
llg p = n;
while(p==n && p) p = pollardRho(p, rand()%(n-1)+1);
findFac(p);
findFac(n
/p);
}
}

int main()
{
int t, i;
llg n, ans;
srand((
int)time(0));
scanf(
"%d", &t);
while(t--)
{
scanf(
"%I64d", &n);
if(millerRabin(n)) printf("Prime\n");
else
{
tot
=0;
findFac(n);
ans
= n;
for(i =0; i < tot; i++) ans = min(ans, p[i]);
printf(
"%I64d\n", ans);
}
}
return0;
}

  

pku2429:

和1181差不多,令n=lcm/gcd,对n进行因子分解,最后暴力枚举所有小于sqrt(n)的因子x,另一个为n/x,然后输出x*a  n/x*a。刚开始写dfs时想剪一下枝,结果把正确的解给剪掉了=_=!,不用剪枝也可以300ms左右,看rp~~

View Code

 

pku1006:

第二次做这个题,以前使用暴力模拟做得,刚用中国剩余定理写了一下,结果以前用时79ms,中国剩余定理110ms。。。

主要算是再重写一遍扩展欧几里德吧,好久没写了,突然忘了怎么写了,在纸上现推了一遍才写的=_=~

View Code
#include <iostream>
#include
<cstdio>
#include
<cstring>
usingnamespace std;

constint M =21252;
constint m[4] = {0, 23, 28, 33};

int d, a[4];

int extend_gcd(int a, int b, int&x, int&y)
{
int d, t;
if(b ==0)
{
x
=1, y =0;
return a;
}
else
{
d
= extend_gcd(b, a%b, x, y);
t
= x;
x
= y;
y
= t - a/b*y;
return d;
}
}

int main()
{
int x, y, i, mm, ans, Case =0;
while(scanf("%d%d%d%d", a+1, a+2, a+3, &d) != EOF)
{
if(a[1] ==-1) break;
ans
=0;
for(i =1; i <4; i++)
{
mm
= M / m[i];
extend_gcd(mm, m[i], x, y);
ans
= (ans + mm*x*a[i]) % M;
}
if(ans <0) ans += M;
if(ans <= d) ans += M;
printf(
"Case %d: the next triple peak occurs in %d days.\n", ++Case, ans-d);
}
return0;
}

  

pku2891:

ax=b(mod n)方程组情形:

View Code
#include <iostream>
#include
<cstdio>
#include
<cstring>
usingnamespace std;

constint N =10010;

typedef
longlong llg;

llg k, a[N], r[N];

llg Abs(llg x)
{
if(x <0) x =-x;
return x;
}

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

llg lcm(llg a, llg b)
{
llg d
= gcd(a, b);
return a/d*b;
}

llg extend_gcd(llg a, llg b, llg
&x, llg &y)
{
llg d, t;
if(b ==0)
{
x
=1, y =0;
return a;
}
else
{
d
= extend_gcd(b, a%b, x, y);
t
= x;
x
= y;
y
= t - a/b*y;
return d;
}
}

bool modular(llg a, llg n, llg b, llg &tx)
{
llg d, x, y;
d
= extend_gcd(Abs(a), Abs(n), x, y);
if(b%d !=0) returnfalse;
else
{
x
= x*(b/d);
x
= (x%n+n)%n;
tx
= x*a;
}
returntrue;
}

int main()
{
int i;
llg x, tmp;
bool flag;
while(scanf("%I64d", &k) != EOF)
{
for(i =1; i <= k; i++)
{
scanf(
"%I64d%I64d", a+i, r+i);
r[i]
%= a[i];
}
if(k ==1) printf("%I64d\n", r[1]%a[1]);
else
{
flag
=true;
for(i =2; i <= k; i++)
{
flag
= modular(a[i-1], a[i], r[i]-r[i-1], x);
if(!flag) break;
x
+= r[i-1];
tmp
= lcm(a[i-1], a[i]);
r[i]
= (x%tmp+tmp)%tmp;
a[i]
= tmp;
}
if(flag) printf("%I64d\n", r[k]);
else printf("-1\n");
}
}
return0;
}

  

 

 

 

 

 

 

 

 

 

 

 

posted on 2011-08-03 14:41  Moon_1st  阅读(541)  评论(0编辑  收藏  举报

导航