0%

问题描述:

给你一组形如下图的方程组: EXCRT

你需要解出最小的$x$。

输入:

有多组数据,每组数据以$n$开始,表示有$n$个方程组 接下来n行分别表示$a_1$,$m_i$

输出:

输出最小的$x$

思路:

这就是扩展中国剩余定理的模板题了。如果$m_i$都两两互质,那么就是直接中国剩余定理就好了。但这里$m_i$是随机的,所以要用到一些其他的处理手法。

这里我们可以先用扩展欧几里得求出第一组方程:$x+y*m_1=a_1$。这样,我们得到了一组通解$x=x_0+t*m_1$。我们利用这组通解带入第二组方程,得到:变形为:于是我们得到了另一个线性同余方程,并且可以解出$t$的通解。利用这个通解又可以代入下一个方程。于是我们可以这样推出整个方程的解。

用数学归纳法再严谨地说一遍做法:设已经求出了$k$组方程,$ m_0= \prod_{i=1}^{k} m_i$,前$k$组方程的通解为$x=x_0+t*m_0$。那么我们可以将通解代入第$k+1$组方程中,得到 再利用扩展欧几里得解出新方程的通解。于是,我们可以利用$n$次扩展欧几里得解出计算出答案。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define LL long long
using namespace std;

LL n, a[10000005], r[10000005];

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

LL solve()
{
LL x=0, y=0, x0=0, m0=1;
for(int i=1; i<=n; i++)
{
LL d=exgcd(m0, a[i], x, y);
if((r[i]-x0)%d) return -1;
x=(r[i]-x0)/d*x%a[i];
x0=(x0+x*m0)%(m0/d*a[i]);
m0=m0/d*a[i];
}
return (x0+m0)%m0;
}

int main()
{
while(scanf("%lld", &n)!=EOF)
{
for(int i=1; i<=n; i++) scanf("%lld%lld", &a[i], &r[i]);
printf("%lld\n", solve());
}
}

问题描述:

我们要找出一个最小的 $L$ 的只由$8$构成的数,使它是$L$的整数倍。

输入:

由多组数据,每组数据一个数 $L$,输入以$0$结束 $L$($1≤L≤2000000000$)

输出:

一个数,表示数字的长度。如果不存在则输出$0$

思路:

一道比较玄学的题。这种只由一种数字构成相关问题都是一个套路吧。

先是将$8888…8$看成$(10^k-1)/9*8$。于是 $9L|8(10^k-1)$,等价为$10^k \equiv 1 (mod \frac{9L}{gcd(8, L)})$,这是一步特别重要的转化。

然后有这样的一个模型:若正整数$a, n$互质,那么满足$a^k \equiv 1 (mod n)$ 的最小正整数$k_0$一定是$ \varphi (n)$ 的约数。

因此,$k$一定为 $\varphi ( \frac{9L}{gcd(8, L)} ) $的约数。然后我们就可以枚举约数,再使用快速幂验证就OK了。有一个细节就是 $\varphi (n)$会大于MAX_INT所以要注意要再用快速乘防止爆$long long$。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include 
#include
#include
#include
#define LL long long
using namespace std;

LL l, Case, fac[20000005];

LL gcd(LL x, LL y)
{
return y==0 ? x : gcd(y, x%y);
}

LL phi(LL n)
{
LL ans=n;
for(LL i=2; i*i<=n; i++)
if(n%i==0)
{
ans=ans/i*(i-1);
while(n%i==0) n/=i;
}
if(n>1) ans=ans/n*(n-1);
return ans;
}

LL ksc(LL x, LL y, LL p)
{
LL ans=0;
x%=p; y%=p;
while(y)
{
if(y&1) ans=(ans+x)%p;
x=(x+x)%p; y>>=1;
}
return ans;
}

LL ksm(LL x, LL y, LL p)
{
LL ans=1;
while(y)
{
if(y&1) ans=ksc(ans, x, p);
x=ksc(x, x, p); y>>=1;
}
return ans;
}

int main()
{
while(scanf("%lld", &l) && l)
{
int flag=0, cnt=0;;
LL t1=l*9/gcd(l, 8);
LL t2=phi(t1);
for(LL i=1; i*i<=t2; i++)
if(t2%i==0) fac[++cnt]=i, fac[++cnt]=t2/i;
sort(fac+1, fac+cnt+1);
for(LL i=1; i<=cnt; i++)
if(ksm(10, fac[i], t1)==1)
{
printf("Case %lld: %lld\n", ++Case, fac[i]);
flag=1;
break;
}
if(!flag) printf("Case %lld: 0\n", ++Case);
}
}

问题描述:

给出正整数$n$和$k$,计算$G(n, k) =k \% 1 + k \% 2 + … + k \% n$的值。

输入:

输入仅一行,包含两个整数$n$, $k$ ($1<=n, k<=10^9$)

输出:

输出仅一行,即$j(n, k)$。

思路:

直接计算原式肯定是会T的。。。我们可以根据 $a \% b=a-\lfloor a/b \rfloor *b$ 将原式变形。所以答案可以在$n*k$的基础上减去上式的后一部分得到。然后我们可以发现 $\lfloor a/b \rfloor$ 的取值是一段一段的,所以可以对于每一段用等差数列求和的公式得出答案。

复杂度似乎是$O( \sqrt{n} )$的

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include 
#define LL long long
using namespace std;

LL n, k, ans;

int main()
{
scanf("%lld%lld", &n, &k);
ans=n*k;
if(n>k) n=k;
for(LL l=1, r; l<=n; l=r+1)
{
LL temp=k/l; r=min(k/temp, n);
ans-=temp*(l+r)*(r-l+1)/2;
}
printf("%lld\n", ans);
}

问题描述:

 对于任何正整数$x$,其约数的个数记作$g(x)$。如果某个正整数$x$满足:$g(x)>g(i)$ 其中$0<i<x$,则称x为反质数。现在给定一个数$N$你需要找出不大于$N$的最大反素数。

输入:

一个数$N$($1<=N<=2000000000$)。

输出:

不超过N的最大的反质数。

思路:

其实我们要求的就是$1-N$内约数个数最多的数(如果个数一样就是小的那个),然后我们可以利用一些神奇的性质用搜索解出答案。

首先反素数的质因子是不会超过10个的,并且质因子的指数是单调递减的。利用这两个性质缩小搜索范围,然后直接dfs枚举它的质因子就好了。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include 
#define LL long long
using namespace std;

const int prime[]={2, 3, 5, 7, 11, 13, 17, 19, 23, 29};

LL n, maxsum, ans;

LL ksm(LL x, LL y)
{
LL sum=1;
while(y)
{
if(y&1) sum*=x;
y>>=1; x*=x;
}
return sum;
}

void dfs(int id, int cnt, int sum, LL num)
{
if(id==11)
{
if(sum>maxsum) ans=num, maxsum=sum;
if(sum==maxsum) if(numn) break;
dfs(id+1, i, sum*(i+1), num*ksm(prime[id], i));
}
}

int main()
{
scanf("%lld", &n);
dfs(0, 30, 1, 1);
printf("%lld\n", ans);
}

问题描述:

我们定义素数的距离为两个相邻素数之间存在合数的个数。给你一个区间$[l, r]$你需要求出区间内距离最大和最小的两对素数

输入:

有多组数据,每组数据两个数l, r 其中l, r不超过2147483647,区间大小不超过1000000

输出:

如果不存在两个质数输出”There are no adjacent primes.” 否则输出 “x1,x2 are closest, y1,y2 are most distant.”其中x1,x2,y1,y2为两对距离最大和最小的素数

思路:

由于l, r都很大,所以不方便之间线性筛。但区间大小不超过1000000,所以我们要利用好这个条件。

由于区间较小,我们可以利用类似于筛的方法筛掉区间的合数。我们可以先预处理出$2^{16}$内的素数,对于每一个区间我们通过枚举素数$prime[i]$,把区间内为$prime[i]$的倍数标记。枚举了所有素数后,区间内没有标记的数就是素数了。然后$O(n)$扫一遍计算出答案。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#define MAXN 2000005
#define LL long long
using namespace std;

int l, r, tot, v[MAXN], prime[MAXN];
bool vis[1000005];

void getprime(int n)
{
for(int i=2; i<=n; i++)
{
if(!v[i]) prime[++tot]=i, v[i]=i;
for(int j=1; j<=tot; j++)
{
if(prime[j]>v[i] || prime[j]>n/i) break;
v[i*prime[j]]=prime[j];
}
}
}

int main()
{
getprime(MAXN-5);
while(scanf("%d%d", &l, &r)!=EOF)
{
if(l==1) l++;
int ans1, ans2, ans3, ans4, last=-1, maxn=-1, minn=1e9;
memset(vis, 0, sizeof(vis));
for(int j=1; j<=tot; j++)
{
int x=((LL)l+prime[j]-1)/prime[j], y=r/prime[j];
for(int i=max(2, x); i<=y; i++) vis[prime[j]*i-l]=1;
}

for(int i=0; i<=r-l; i++)
{
if(vis[i]) continue;
if(last==-1) {last=i; continue;}
if(i-last>maxn)
{
maxn=i-last;
ans1=last+l; ans2=i+l;
}
if(i-last<minn)
{
minn=i-last;
ans3=last+l; ans4=i+l;
}
last=i;
}
if(maxn==-1) printf("There are no adjacent primes.\n");
else printf("%d,%d are closest, %d,%d are most distant.\n", ans3, ans4, ans1, ans2);
}
}