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。
#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就可以了。
#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。也就是说,可以利用互质的周期性来求解。
#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)),对于合数同样也成立。如果知道这个定理的话,那剩下的就很简单啦。
pku1181:
需要miller-rabin和pollard-rho,miller-rabin的原理倒是理解,不过pollard还不是很明白,先当模版了。。。这道题G++RE的很诡异啊,不知道为什么,把堆栈的深度控制在100也会RE,汗啊。。。但是C++却能AC,不知道两种编译器内在这有什么差异~。
还有就是,从这个题可以看出,取模运算确实是慢啊。。。看到某牛写的code,加了个常数优化可以砍掉500+ms~
#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~~
pku1006:
第二次做这个题,以前使用暴力模拟做得,刚用中国剩余定理写了一下,结果以前用时79ms,中国剩余定理110ms。。。
主要算是再重写一遍扩展欧几里德吧,好久没写了,突然忘了怎么写了,在纸上现推了一遍才写的=_=~
#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)方程组情形:
#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;
}