基础数论专题题解集(暂未全部AC)

A - 青蛙的约会

题面

两只青蛙在网上相识了,它们聊得很开心,于是觉得很有必要见一面。它们很高兴地发现它们住在同一条纬度线上,于是它们约定各自朝西跳,直到碰面为止。可是它们出发之前忘记了一件很重要的事情,既没有问清楚对方的特征,也没有约定见面的具体位置。不过青蛙们都是很乐观的,它们觉得只要一直朝着某个方向跳下去,总能碰到对方的。但是除非这两只青蛙在同一时间跳到同一点上,不然是永远都不可能碰面的。为了帮助这两只乐观的青蛙,你被要求写一个程序来判断这两只青蛙是否能够碰面,会在什么时候碰面。
我们把这两只青蛙分别叫做青蛙A和青蛙B,并且规定纬度线上东经0度处为原点,由东往西为正方向,单位长度 1 米,这样我们就得到了一条首尾相接的数轴。设青蛙A的出发点坐标是 x,青蛙B的出发点坐标是 y 。青蛙A一次能跳 m 米,青蛙B一次能跳 n 米,两只青蛙跳一次所花费的时间相同。纬度线总长 L 米。现在要你求出它们跳了几次以后才会碰面。

题意

有一个总长为 L 的环形数轴,青蛙A从 x 出发,每次跳 m 米,青蛙B从 y 出发,每次跳 n 米,问最少跳多少次可以相遇(可能存在永远不相遇的情况)

输入格式 - Input

输入只包括 5 个整数x,y,m,n,L,其中xy<2000000000, 0<m,n<2000000000, 0<L<2100000000

输出格式 - Output

输出碰面所需要的跳跃次数,如果永远不可能碰面则输出一行"Impossible"

样例 - Sample

Input Output
1 2 3 4 5 4

思路

由题意可得,设 A 和 B 的跳跃次数为 c,A 和 B 经过长度为 L 的环形数轴 d1d2 次, 最终停在数字 s
可得 x+mc=Ld1+sy+nc=Ld2+s
合并得 xy+(mn)c=L(d1d2)
(mn)c+L(d2d1)yx (mod L)

问题转化为求未知数为 c(d2d1) 的线性同余方程,由扩展欧几里得算法可得
m0n0d (mod L),若 d | yx 则方程有解,反之无解
由扩展欧几里得得出 d 后,x=((x0yxd)%Ld+Ld)%Ld

代码

Status Time Memory Length Lang
Accepted
16ms 368kB 673 G++
点击查看代码
#include <iostream>
using namespace std;

typedef long long ll;

inline ll exGCD(ll a, ll b, ll &x, ll &y)
{
	if (!b)
	{
		x = 1, y = 0;
		return a;
	}
	
	ll d = exGCD(b, a % b, x, y);
	ll temp = x;
	
	x = y;
	y = temp - (a / b) * y;
	
	return d;
}

int main()
{
	#ifdef WatPz
	string _fn = "A";
	
	freopen((_fn + ".in").c_str(), "r", stdin);
	freopen((_fn + ".out").c_str(), "w", stdout);
	#endif
	
	ll x, y, m, n, L, rx, ry;
	cin >> x >> y >> m >> n >> L;
	
	ll d = exGCD(n - m, L, rx, ry), t;
	if ((y - x) % d == 0)
	{
		rx = rx * (x - y) / d;
		t = L / d;
		
		rx = (rx % t + t) % t;
		cout << rx << endl;
	}
	else
	{
		cout << "Impossible" << endl;
	}
	
	
	return 0;
}

C - 荒岛野人

题面

克里特岛以野人群居而著称。岛上有排列成环行的 M 个山洞。这些山洞顺时针编号为 1,2,,M
岛上住着 N 个野人,一开始依次住在山洞 C1,C2,,CN 中,以后每年,第 i 个野人会沿顺时针向前走 Pi 个洞住下来。每个野人 i 有一个寿命值 Li,即生存的年数。下面四幅图描述了一个有 6 个山洞,住有 3 个野人的岛上前 4 年的情况。三个野人初始的洞穴编号依次为 1,2,3;每年要走过的洞穴数依次为 3,7,2;寿命值依次为 4,3,1

vs880I.md.png

奇怪的是,虽然野人有很多,但没有任何两个野人在有生之年处在同一个山洞中,使得小岛一直保持和平与宁静,这让科学家们很是惊奇。他们想知道,至少有多少个山洞,才能维持岛上的和平呢?

题意

有很多野人,第 i 个野人初始位置为第 Ci 个山洞, 每年走 Pi 个山洞,寿命为 Li
问你最少需要多少个山洞才能让野人两两不相遇

输入格式 - Input

第一行为一个整数 N,即野人的数目;
第二行到第 N+1 每行为三个整数 Ci,Pi,Li,表示每个野人所住的初始洞穴编号,每年走过的洞穴数及寿命值。

输出格式 - Output

仅包含一个数 M,即最少可能的山洞数。输入数据保证有解,且 M 不大于 106

样例 - Sample

Input Output
3
1 3 4
2 7 3
3 2 1
6

思路

对于两个野人 a,b,设经过 x 年后相遇,分别绕整个山洞群落 d1d2 次,停在山洞 c
可得 Ca+Pax=Md1+cCb+Pbx=Md2+c
(PaPb)x+M(d1d2)CbCa (mod M)

根据 exGCD 算出 x 的最小整数解后,若 xmin{L1,L2}a,b 相遇,反之不相遇

定义函数 check(a,b) 判断 a,b 是否相遇,令 ans=max{C1,C2,,CN}
判断是否所有野人不会相遇:是则输出 ans;否则 ans+1,重复判断操作
(题目保证有解且 ans<=106,无须判断无解情况)

代码

Status Time Memory Length Lang
Accepted
115ms 364kB 1208 G++ (ISO C++20) -O2 64bit
点击查看代码
#include <iostream>
using namespace std;

#define N 17

#define MAX(a,b) ((a) > (b) ? (a) : (b))
#define MIN(a,b) ((a) < (b) ? (a) : (b))

typedef long long ll;

ll n, c[N], p[N], l[N], maxc;

ll exGCD(ll a, ll b, ll &x, ll &y)
{
	if (!b)
	{
		x = 1, y = 0;
		return a;
	}
	
	ll d = exGCD(b, a % b, x, y);
	ll t = x;
	
	x = y;
	y = t - (a / b) * y;
	
	return d;
}

bool check(ll i, ll j, ll M)
{
	ll x, y;
	ll g = exGCD(p[j] - p[i], M, x, y);
	
	if ((c[i] - c[j]) % g == 0)
	{
		x *= (c[i] - c[j]) / g;
		y = M / g;
		
		if (y < 0) y = -y;
		
		x = (x % y + y) % y;
		
		return x > MIN(l[i], l[j]);
	}
	else
		return true;
}

bool allcheck(ll M)
{
	for (ll i = 1; i <= n; i++)
	{
		for (ll j = i + 1; j <= n; j++)
		{
			if (!check(i, j, M))
			{
				return false;
			}
		}
	}
	
	return true;
}

signed main()
{
	#ifdef WatPz
	string _fn = "C";
	
	freopen((_fn + ".in").c_str(), "r", stdin);
	freopen((_fn + ".out").c_str(), "w", stdout);
	#endif
	
	scanf("%lld", &n);
	
	maxc = 0;
	for (ll i = 1; i <= n; i++)
	{
		scanf("%lld%lld%lld", &c[i], &p[i], &l[i]);
		maxc = MAX(maxc, c[i]);
	}
	
	for (ll i = maxc; i <= 1000000; i++)
	{
		if (allcheck(i))
		{
			printf("%lld", i);
			break;
		}
	}
	
	return 0;
}

D - Bi-shoe and Phi-shoe

题面

Bamboo Pole-vault is a massively popular sport in Xzhiland. And Master Phi-shoe is a very popular coach for his success. He needs some bamboos for his students, so he asked his assistant Bi-Shoe to go to the market and buy them. Plenty of Bamboos of all possible integer lengths (yes!) are available in the market. According to Xzhila tradition,

Score of a bamboo = Φ (bamboo's length)

(Xzhilans are really fond of number theory). For your information, Φ (n) = numbers less than n which are relatively prime (having no common divisor other than 1) to n. So, score of a bamboo of length 9 is 6 as 1, 2, 4, 5, 7, 8 are relatively prime to 9.

The assistant Bi-shoe has to buy one bamboo for each student. As a twist, each pole-vault student of Phi-shoe has a lucky number. Bi-shoe wants to buy bamboos such that each of them gets a bamboo with a score greater than or equal to his/her lucky number. Bi-shoe wants to minimize the total amount of money spent for buying the bamboos. One unit of bamboo costs 1 Xukha. Help him.

题意

你要去给 n 个学生买竹子,第 i 个学生的幸运数字为 pi,所需要的竹子长度为 Li
需满足 i[1,n],Φ(Li)>=pi(全部学生均满足 Φ(竹子长度) >= 幸运数字)

i=1nLi(就是竹子总长度)的最小值

输入格式 - Input

Input starts with an integer T(100), denoting the number of test cases.

Each case starts with a line containing an integer n(1n10000) denoting the number of students of Phi-shoe. The next line contains n space separated integers denoting the lucky numbers p1,p2,,pn for the students. Each lucky number pi will lie in the range [1,106].

第一行为 1 个数字 T(100),代表测试样例的数量
每个样例的第一行为 1 个数字 n(1n10000),代表有 n 个学生
每个样例的第二行为 n 个数字:p1,p2,,pn(1pi106),为对应学生的幸运数字

输出格式 - Output

For each case, print the case number and the minimum possible money spent for buying the bamboos. See the samples for details.

对于每个测试样例,输出满足要求的最短竹子总长度(应符合样例的格式)

样例 - Sample

Input Output
3
5
1 2 3 4 5
6
10 11 12 13 14 15
2
1 1
Case 1: 22 Xukha
Case 2: 88 Xukha
Case 3: 4 Xukha

思路

对于数字 x

x 为素数时, 有 Φ(x)=x1
x 不为素数时,设 y 为小于 x 的最大素数,有 Φ(x)Φ(y)

因此对于 pi

pi+1 为素数时,有 Lipi+1
pi+1 不为素数时,设 c 为大于 pi+1的最小素数,有 Lic

综上,使用素数筛法后,从尾到头预处理答案即可

代码

Status Time Memory Length Lang
Accepted
142ms 21008kB 1028 C++ 17 (g++ 7.5.0)
点击查看代码
#include <iostream>
#include <algorithm>
using namespace std;

#define N 1000004

typedef long long ll;;

bool p[N + 2];
ll phi[N + 2];
ll cnt, prime[N + 2];

void Get_ol()
{
	for (ll i = 2; i < N; i++)
	{
		if (!p[i])
		{
			prime[++cnt] = i;
			phi[i] = i - 1;
		}
		
		for (ll j = 1; j <= cnt && i * prime[j] < N; j++)
		{
			p[i * prime[j]] = 1;
			
			if (i % prime[j] == 0)
			{
				phi[i * prime[j]] = phi[i] * prime[j];
				
				break;
			}
			
			phi[i * prime[j]] = phi[i] * phi[prime[j]];
		}
	}
}

ll _, n, a, ans[N + 2];
ll r_ans = 0;

int main()
{
	#ifdef WatPz
	string _fn = "D";
	
	freopen((_fn + ".in").c_str(), "r", stdin);
	freopen((_fn + ".out").c_str(), "w", stdout);
	#endif
	
	Get_ol();
	
	ll k = prime[cnt];
	
	for (ll i = k - 1; i >= 1; i--)
	{
		ans[i] = k;
		if (!p[i]) k = i;
	}
	
	cin >> _;
	
	for (ll __ = 1; __ <= _; __++)
	{
		cin >> n;
		
		r_ans = 0;
		for (ll i = 1; i <= n; i++)
		{
			cin >> a;
			
			r_ans += ans[a];
		}
		
		printf("Case %lld: %lld Xukha\n", __, r_ans);
	}
	
	return 0;
}

H - Relatives

题面

Given n, a positive integer, how many positive integers less than n are relatively prime to n? Two integers a and b are relatively prime if there are no integers x>1,y>0,z>0 such that a=xy and b=xz.

题意

给定一个正整数 n,求 card({x | xN,x[1,n1]  GCD(x,n)=1})
即求 范围在 [1,n1] 内有多少正整数与 n 互质(最大公因数 GCD 为1)

输入格式 - Input

There are several test cases. For each test case, standard input contains a line with n <= 1,000,000,000. A line containing 0 follows the last case.

多组样例,每组样例包含 1 个正整数 n0,后者代表样例结束

输出格式 - Output

For each test case there should be single line of output answering the question posed above.

对于每组样例,输出上面问题的答案

样例 - Sample

Input Output
7
12
0
6
4

思路

题目的问题实际上是让你求:对于给定的 nΦ(n)的值为多少

写一个欧拉函数即可解决

代码

Status Time Memory Length Lang
Accepted
0ms 328kB 543 G++
点击查看代码
#include <iostream>
using namespace std;

typedef long long ll;

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

ll n;

int main()
{
	#ifdef WatPz
	string _fn = "H";
	
	freopen((_fn + ".in").c_str(), "r", stdin);
	freopen((_fn + ".out").c_str(), "w", stdout);
	#endif
	
	while (1)
	{
		scanf("%lld", &n);
		
		if (!n) break;
		
		printf("%lld\n", phi(n));
	}
	
	return 0;
}

I - GCD

题面

The greatest common divisor GCD(a,b) of two positive integers a and b, sometimes written (a,b), is the largest divisor common to a and b, For example, (1,2)=1, (12,18)=6.
(a,b) can be easily found by the Euclidean algorithm. Now Carp is considering a little more difficult problem:
Given integers N and M, how many integer X satisfies 1XN and (X,N)M.

题意

给你两个正整数 NM,求 card({X | XN,1XN  GCD(X,N)M})
即求 范围在 [1,N] 内有多少正整数与 N最大公约数 大于等于 M

输入格式 - Input

The first line of input is an integer T(T100) representing the number of test cases. The following T lines each contains two numbers N and M (2N1000000000,1MN), representing a test case.

第一行为 1 个整数 T(T100),代表测试样例的数量
每个样例包含 2 个整数 N(2N1000000000)M(1MN)

输出格式 - Output

For each test case there should be single line of output answering the question posed above.

对于每组样例,输出上面问题的答案

样例 - Sample

Input Output
3
1 1
10 2
10000 72
1
6
260

思路

不妨设 GCD(X,N)=P 则可得 GCD(XP,NP)=1XPNP 互素
X[1,N] 可得 P | N(即 PN 的约数),Φ(NP)=card({X | XN,X[1,N]  GCD(XP,NP)=1})

GCD(X,N)=PM 时,Φ(NP)= 满足该条件的 X 数量

综上,循环遍历 N 的所有约数,设其中大于等于 M 的数字为 P0
ans=ans+Φ(NP0) 即可

代码

Status Time Memory Length Lang
Accepted
31ms 1372kB 755 G++
点击查看代码
#include <iostream>
using namespace std;

typedef long long ll;

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

ll _, n, m, ans;

int main()
{
	#ifdef WatPz
	string _fn = "I";
	
	freopen((_fn + ".in").c_str(), "r", stdin);
	freopen((_fn + ".out").c_str(), "w", stdout);
	#endif
	
	scanf("%lld", &_);
	
	while (_--)
	{
		scanf("%lld%lld", &n, &m);
		
		ans = 0;
		
		for (ll p = 1; p * p <= n; p++)
		{
			if (n % p == 0)
			{
				if (p >= m)
					ans += phi(n / p);
				
				if (n / p >= m && p != n / p)
					ans += phi(p);
				
			}
		}
		
		printf("%lld\n", ans);
	}
	
	return 0;
}

J - Leading and Trailing

题面

You are given two integers: n and k, your task is to find the most significant three digits, and least significant three digits of nk.

题意

给出两个正整数 nk,求 nk 的前三位数字和后三位数字

输入格式 - Input

Input starts with an integer T(1000), denoting the number of test cases.

Each case starts with a line containing two integers: n(2n<231) and k(1k107).

第一行为 1 个整数 T(1000),代表测试样例的数量
每个样例包含 2 个整数 n(2n<231)k(1k107)

输出格式 - Output

For each case, print the case number and the three leading digits (most significant) and three trailing digits (least significant). You can assume that the input is given such that nk contains at least six digits.

对于每组样例,输出 nk 的前三位数字和后三位数字

样例 - Sample

Input Output
5
123456 1
123456 2
2 31
2 32
29 8751919
Case 1: 123 456
Case 2: 152 936
Case 3: 214 648
Case 4: 429 296
Case 5: 665 669

思路

对于后三位数字,可以考虑使用 qpow 快速幂算法,将取模的 MOD 设为 1000

对于前三位数字,设 nk=a×10m
可得 log10(nk)=log10(a×10m)(a<10)
化简得 k×log10(n)=log10(a)+m

则:
m=k×log10(n)
log10(a)=k×log10(n)m
a=10log10(a)

归纳得:
a=10k×log10(n)k×log10(n)
前三位数字 =a100

(因为 nk 的十位和百位可能出现 0,因此输出时需要输出 "%03lld" 而不是 "%lld")

代码

Status Time Memory Length Lang
Accepted
5ms 648kB 699 C++ 17 (g++ 7.5.0)
点击查看代码
#include <iostream>
#include <cmath>
using namespace std;

#define isDigit(x) ('0' <= x && x <= '9')

typedef long long ll;

ll qpow(ll x, ll power, ll mod)
{
	x %= mod;
	
	ll res = 1;
	for (; power; power >>= 1, (x *= x) %= mod)
	{
		if (power & 1)
			(res *= x) %= mod;
	}
	
	return res;
}

ll _, n, k;

int main()
{
	#ifdef WatPz
	string _fn = "J";
	
	freopen((_fn + ".in").c_str(), "r", stdin);
	freopen((_fn + ".out").c_str(), "w", stdout);
	#endif
	
	scanf("%lld", &_);
	
	for (ll __ = 1; __ <= _; __++)
	{
		scanf("%lld%lld", &n, &k);
		
		double kp = 1.0 * k * log10(n);
		kp -= (ll) kp;
		
		printf("Case %lld: %lld %03lld\n", __, ll(pow(10, kp) * 100), qpow(n, k, 1000));
	}
	
	return 0;
}

K - Goldbach's Conjecture

题面

Goldbach's conjecture is one of the oldest unsolved problems in number theory and in all of mathematics.

It states:
Every even integer, greater than 2, can be expressed as the sum of two primes.

Your task is to check whether this conjecture holds for integers up to 107.

题意

给你一个数字 nn 为偶数),设素数集合为 P
请你求出存在多少组 (i,j)(ij  i,jP) 满足 强哥德巴赫猜想(即 i+j=n

输入格式 - Input

Input starts with an integer T(300), denoting the number of test cases.

Each case starts with a line containing an integer n(4n107,n is even).

第一行为 1 个整数 T(300),代表测试样例的数量
每个样例包含 1 个整数 n(4n107,n 是偶数)

输出格式 - Output

For each case, print the case number and the number of ways you can express n as sum of two primes. To be more specific, we want to find the number of (a, b) where:

  • Both a and b are prime,
  • a + b = n and
  • a ≤ b.

对于每组样例,输出能够满足强哥德巴赫猜想的 (i,j) 数量(关于 (i,j) 的定义见上文)

样例 - Sample

Input Output
2
6
4
Case 1: 1
Case 2: 1

思路

因为 强哥德巴赫猜想 现在数学界都没法完全证明,所以就别想着推什么通解了233
直接暴力循环所有质数就行 xD

代码

Status Time Memory Length Lang
Accepted
592ms 52240kB 897 C++ 17 (g++ 7.5.0)
点击查看代码
#include <iostream>
using namespace std;

#define N 10000000
#define pp (9999991 >> 1)

bool p[N];
int phi[N];
int cnt, prime[664585];

void Get_ol()
{
	for (int i = 2; i < N; i++)
	{
		if (!p[i])
		{
			prime[++cnt] = i;
			phi[i] = i - 1;
		}
		
		for (int j = 1; j <= cnt && i * prime[j] < N; j++)
		{
			p[i * prime[j]] = 1;
			
			if (i % prime[j] == 0)
			{
				phi[i * prime[j]] = phi[i] * prime[j];
				
				break;
			}
			
			phi[i * prime[j]] = phi[i] * phi[prime[j]];
		}
	}
}

int _, a, tt;

int main()
{
	#ifdef WatPz
	string _fn = "K";

	freopen((_fn + ".in").c_str(), "r", stdin);
	freopen((_fn + ".out").c_str(), "w", stdout);
	#endif
	
	Get_ol();
	
	scanf("%d", &_);
	
	for (int __ = 1; __ <= _; __++)
	{
		scanf("%d", &a);
		tt = 0;
		
		for (int j = 1; prime[j] <= (a >> 1); j++)
		{
			if (!p[a - prime[j]])
				tt++;
		}
		
		printf("Case %d: %d\n", __, tt);
	}
	
	return 0;
}

L - Harmonic Number (II)

题面

I was trying to solve problem N - Harmonic Number, I wrote the following code

long long H( int n ) {
    long long res = 0;
    for( int i = 1; i <= n; i++ )
        res = res + n / i;
    return res;
}

Yes, my error was that I was using the integer divisions only. However, you are given n, you have to find H(n) as in my code.

题意

这个憨憨把 1i 看成了 ni,并且在计算 i=1nni 的时候,用了整数类型
这意味着他将得到错误的答案 i=1nni

但是呢,如果每个分数都向下取整,求这个答案也很有意思,因此他打算让你解决这个问题
因为他写的屎山代码实在是太慢了 xD

输入格式 - Input

Input starts with an integer T(1000), denoting the number of test cases.

Each case starts with a line containing an integer n(1n<231).

第一行为 1 个整数 T(1000),代表测试样例的数量
每个样例包含 1 个整数 n(1n<231)

输出格式 - Output

For each case, print the case number and H(n) calculated by the code.

对于每组样例,输出 H(n) 的理论运行结果

样例 - Sample

Input Output
11
1
2
3
4
5
6
7
8
9
10
2147483647
Case 1: 1
Case 2: 3
Case 3: 5
Case 4: 8
Case 5: 10
Case 6: 14
Case 7: 16
Case 8: 20
Case 9: 23
Case 10: 27
Case 11: 46475828386

思路

  • 对于 向下取整 这种特殊操作,一般采取 定性分析 的方法
    不妨设 pi=ni,则 ans=i=1npi

  • 通过数形结合的思维,不难发现 pi 的几何意义:
    长度为 n 的长块最多能够分成长度为 i 的长块的数量

  • 因此对于 nxx=pj,jN,j[1,n]),其代数定义为:
    Ax={i | iN,i[1,n],ni=x},则有nx=max{Ax}
    (也就是说 nx 代表的是 满足 x=ni 的所有 i 中的最大值)

    因此满足 x=nii 的数量,也就是 card(Ax),等价于nxnx+1

  • 因此,我们只需要遍历 x 可以取到的所有值,根据 nxnx+1算出数量,再乘上当前的实际贡献 x 就行

  • 但到这里还没有完全解决,n 的最大值是 2311,如果以 O(n) 的复杂度做单组样例
    还是会 TLE,因此继续考虑数形结合(做完了但是没完全做完

    我们可以将 n 视为一个正方形的面积

    in 时,所对应的 pi 只能由 i 得到,且 pin
    i>n 时,所对应的 pi 可由 i其他数字 得到,且 pi<n

  • 因此可以考虑设 xN,x[1,n], 用 x 表示 in 时的 ii>n 时 的 pi

    ans=x=1n(nnxnnx+1)+(nxnx+1)

    需要注意的是,对于求和公式中的任意 x,若 nx=x,则只能计算一次
    别找抄公式啊喂(恼)

  • 所以严谨的公式应该是:

    ans=x=1n{(nnxnnx+1)+(nxnx+1),nx!=x(nxnx+1),nx==x

代码

Status Time Memory Length Lang
Accepted
1201ms 1044kB 667 C++ 17 (g++ 7.5.0)
点击查看代码
#include <iostream>
#include <cstring>
using namespace std;

typedef long long ll;

// sqrt(2147483647) = 46340
ll vis[46350];

ll solve(ll n)
{
	memset(vis, 0, sizeof vis);
	
	ll res = 0, p;
	
	for (ll i = 1; i * i <= n; i++)
	{
		p = n / i, res += (n / p - (n / (p + 1))) * p;
		
		if (n / i != i)
			p = i, res += (n / p - (n / (p + 1))) * p;
	}
	
	return res;
}

ll _, a;

int main()
{
	#ifdef WatPz
	string _fn = "L";
	
	freopen((_fn + ".in").c_str(), "r", stdin);
	freopen((_fn + ".out").c_str(), "w", stdout);
	#endif
	
	scanf("%lld", &_);
	
	for (ll __ = 1; __ <= _; __++)
	{
		scanf("%lld", &a);
		printf("Case %lld: %lld\n", __, solve(a));
	}
	
	return 0;
}

N - Harmonic Number

题面

In mathematics, the nth harmonic number is the sum of the reciprocals of the first n natural numbers:

Hn=1+12+13+14++1n=k=1n1k

In this problem, you are given n, you have to find Hn.

题意

如题,给你 n,求解 Hn

输入格式 - Input

Input starts with an integer T(10000), denoting the number of test cases.

Each case starts with a line containing an integer n(1n108).

第一行为 1 个整数 T(10000),代表测试样例的数量
每个样例包含 1 个整数 n(1n108)

输出格式 - Output

For each case, print the case number and the nth harmonic number. Errors less than 108 will be ignored.

对于每组样例,输出对应的 Hn(精度误差 108

样例 - Sample

Input Output
12
1
2
3
4
5
6
7
8
9
90000000
99999999
100000000
Case 1: 1
Case 2: 1.5
Case 3: 1.8333333333
Case 4: 2.0833333333
Case 5: 2.2833333333
Case 6: 2.450
Case 7: 2.5928571429
Case 8: 2.7178571429
Case 9: 2.8289682540
Case 10: 8.8925358988
Case 11: 18.9978964039
Case 12: 18.9978964139

思路

调和级数这种东西,欧拉给过一个公式,这个公式在 n 很大的时候误差很小
Hnln(n)+C+1/2n(其中 C欧拉常量C0.57721566490153286060651209
因此可以选择小数据打表,大数据使用公式

但是!既然都要打表了,干嘛不直接从头到尾都打表(BDFS是什么√8
真男人不用欧拉的公式,直接打表 xD

看下数据,n 最大值为 108,直接存答案爆炸
可以先试一下 T=1, n=108 时的 clock
Windows 下实测发现是 380ms,因此单次最大查询存答案的思路可行

考虑到 T 的最大值是 10000,假设每次询问的数字是均分的
(这样可以让平均复杂度接近实际复杂度)
那么 nmaxTmax=10000,因此可以考虑每隔 10000 个点记录一次答案

这样单次询问最多跑 10000 个点, 最大询问最多跑 n 个点,相当于两次最大查询
题目限制是 2000ms(3802)ms=760ms<2000ms,AC就完了!

(实际数据因为评测环境是 Linux,跑得反而更快,才 224ms

代码

Status Time Memory Length Lang
Accepted
224ms 772kB 724 C++ 17 (g++ 7.5.0)
点击查看代码
#include <iostream>
using namespace std;

#define P 10000

double val[P + 10];

// n = 1e8, sec ~= 0.4
void solve()
{
	double ans = 0.0;
	int i, j, p;
	
	for (i = 0; i <= P - 1; i++)
	{
		val[i] = ans;
		
		for (j = 1; j <= P; j++)
		{
			p = i * P + j;
			
			ans += 1.0 / p;
		}
	}
	
	val[P] = ans;
}

int _, a;
double ans;

int main()
{
	#ifdef WatPz
	string _fn = "N";
	
	freopen((_fn + ".in").c_str(), "r", stdin);
	freopen((_fn + ".out").c_str(), "w", stdout);
	#endif
	
	solve();
	
	scanf("%d", &_);
	
	for (int __ = 1; __ <= _; __++)
	{
		scanf("%d", &a);
		
		ans = val[a / P];
		
		for (int i = (a - (a % P)) + 1; i <= a; i++)
		{
			ans += 1.0 / i;
		}
		
		printf("Case %d: %.10lf\n", __, ans);
	}
	
	return 0;
}

P - Large Division

题面

Given two integers, a and b, you should check whether a is divisible by b or not. We know that an integer a is divisible by an integer b if and only if there exists an integer c such that a=b×c.

题意

给你 ab,判断 a 能否被 b 整除(即判断 b | a

输入格式 - Input

Input starts with an integer T(525), denoting the number of test cases.

Each case starts with a line containing two integers a(10200a10200) and b(0<|b|2311). Numbers will not contain any leading zeroes.

第一行为 1 个整数 T(525),代表测试样例的数量
每个样例包含 2 个整数 a(10200a10200)b(0<|b|2311)

输出格式 - Output

For each case, print the case number first. Then print divisible if a is divisible by b. Otherwise print not divisible.

对于每组样例,若 b | a 则输出 divisible,否则输出 not divisible

样例 - Sample

Input Output
6
101 101
0 67
-101 101
7678123668327637674887634 101
11010000000000000000 256
-202202202202000202202202 -101
Case 1: divisible
Case 2: divisible
Case 3: divisible
Case 4: not divisible
Case 5: divisible
Case 6: divisible

思路

看到第一眼,a % b == 0 解决
然后一看范围,a 是个高精度数字,咋搞?
很简单,利用同余定理的性质

假设 b | a,则有 a=k×b
对于 k,可将其分解为十进制写法 k=a0×100+a1×101++an×10n
其中 ai 为范围在 [0,9] 的整数

这样的话不难看出,a % b 的结果和 (a0×100+a1×101++an×10n) % b 的结果相同
则只需将 a 按照高位到低位的顺序读入,然后每读入一位取模即可

因为 b 最大值能够达到 int 的极限,所以为了防止溢出应选择 long long

代码

Status Time Memory Length Lang
Accepted
5ms 660kB 951 C++ 17 (g++ 7.5.0)
点击查看代码
#include <iostream>
using namespace std;

#define isDigit(x) ('0' <= x && x <= '9')

typedef long long ll;

ll _, __, len, a[220], b;

inline ll qreadStr()
{
	ll len = 0; char ch = getchar();
	
	while (!isDigit(ch))
		ch = getchar();
	
	while (isDigit(ch))
		a[++len] = ch - '0', ch = getchar();
	
	return len;
}

inline ll qread()
{
	ll res = 0; char ch = getchar();
	
	while (!isDigit(ch))
		ch = getchar();
	
	while (isDigit(ch))
		res = (res << 3) + (res << 1) + ch - '0', ch = getchar();
	
	return res;
}

int main()
{
	#ifdef WatPz
	string _fn = "P";
	
	freopen((_fn + ".in").c_str(), "r", stdin);
	freopen((_fn + ".out").c_str(), "w", stdout);
	#endif
	
	_ = qread();
	
	for (__ = 1; __ <= _; __++)
	{
		len = qreadStr(), b = qread();
		
		ll res = 0;
		for (ll i = 1; i <= len; i++)
		{
			res = (res << 3) + (res << 1) + a[i];
			res %= b;
		}
		
		printf("Case %d: ", __);
		printf(res ? "not divisible\n" : "divisible\n");
	}
	
	return 0;
}

Q - Fantasy of a Summation

题面

If you think codes, eat codes then sometimes you may get stressed. In your dreams you may see huge codes, as I have seen once. Here is the code I saw in my dream.

#include <stdio.h>

int cases, caseno;
int n, K, MOD;
int A[1001];

int main() {
    scanf("%d", &cases);
    while( cases-- ) {
        scanf("%d %d %d", &n, &K, &MOD);
        int i, i1, i2, i3, ... , iK;
        for( i = 1; i <= n; i++ ) scanf("%d", &A[i]);

        int res = 0;
        for( i1 = 1; i1 <= n; i1++ ) {
            for( i2 = 1; i2 <= n; i2++ ) {
                for( i3 = 1; i3 <= n; i3++ ) {
                    ...
                    for( iK = 1; iK <= n; iK++ ) {
                        res = ( res + A[i1] + A[i2] + ... + A[iK] ) % MOD;
                    }
                    ...
                }
            }
        }
        printf("Case %d: %d\n", ++caseno, res);
    }
    return 0;
}

Actually the code was about: 'You are given three integers n,K,MOD and n integers: A1,A2,A3,,An, you have to write K nested loops and calculate the summation of all Ai where i is the value of any nested loop variable.'

题意

这人写代码写魔怔了,居然做梦都会梦到代码 xO
这次他梦到了一个循环嵌套的屎山代码,给你三个数字 n, KMOD
n 代表数组 a 的长度,这意味着你还需要读入他所给的 a1,a2,,an
然后 K 是嵌套的循环数量,MOD 是你需要用于取模的数(因为答案数值可以非常大)

至于 K 层循环是什么意思,参考他给出的代码(我修改了下,不过本质不变)

#define foreach(i) for (i = 1; i <= n; i++)

foreach(i1)
  foreach(i2)
    foreach(i3)
      ...
        foreach(ik)
          ans = (ans + a[i1] + a[i2] + a[i3] + ... + a[ik]) % MOD;

输入格式 - Input

Input starts with an integer T(100), denoting the number of test cases.

Each case starts with three integers: n(1n1000),K(1K<231),MOD(1MOD35000). The next line contains n non-negative integers denoting A1,A2,A3,,An. Each of these integers will be fit into a 32 bit signed integer.

第一行为 1 个整数 T(100),代表测试样例的数量
每个样例的第一行包含 3 个整数 n(1n1000),K(1K<231),MOD(1MOD35000)
第二行包含 n 个整数 A1,A2,A3,,An

输出格式 - Output

For each case, print the case number and result of the code.

对于每组样例,输出 res 变量的理论最终值

样例 - Sample

Input Output
2
3 1 35000
1 2 3
2 3 35000
1 2
Case 1: 6
Case 2: 36

思路

这题重点在于推导关于 K 的公式
不妨设 SK 表示 K 层循环时的答案

则:
S1= 数组 a 的总和
S2=n×S1+n(21)S1
S3=n×S2+n(31)S1
...
Si=n×Si1+n(i1)S1

使用归纳法可得
Sk=n(k1)S1+(k1)n(k1)S1=k×n(k1)S1

因此 res=Sk=k×n(k1)S1

计算数组 a 总和 S1 后,使用快速幂计算 k×n(k1) 即可

代码

Status Time Memory Length Lang
Accepted
12ms 648kB 811 C++ 17 (g++ 7.5.0)
点击查看代码
#include <iostream>
#include <cstdio>
using namespace std;

typedef long long ll;

ll _, n, k, MOD, sum, ans, a;

ll qpow(ll x, ll power)
{
	x %= MOD;
	
	ll res = 1;
	
	for (; power; power >>= 1, (x *= x) %= MOD)
	{
		if (power & 1)
			(res *= x) %= MOD;
	}
	
	return res;
}

// i = 1~k, Si = nS(i - 1) + n^(i - 1)S = in^(i - 1)S
// Sk = kn^(k - 1)S

signed main()
{
	#ifdef WatPz
	string _fn = "Q";
	
	freopen((_fn + ".in").c_str(), "r", stdin);
	freopen((_fn + ".out").c_str(), "w", stdout);
	#endif
	
	scanf("%lld", &_);
	
	for (ll __ = 1; __ <= _; __++)
	{
		scanf("%lld%lld%lld", &n, &k, &MOD);
		
		sum = 0;
		for (ll i = 1; i <= n; i++)
		{
			scanf("%lld", &a);
			(sum += a) %= MOD;
		}
		
		ans = (((k * qpow(n, k - 1)) % MOD) * sum) % MOD;
		
		printf("Case %lld: %lld\n", __, ans);
	}
	
	return 0;
}
# ###题面

题意

输入格式 - Input

第一行为 1 个整数 T(525),代表测试样例的数量
每个样例包含 3 个整数 a(10200a10200)b(0<|b|2311)

输出格式 - Output

对于每组样例,

样例 - Sample

Input Output

思路

代码

Status Time Memory Length Lang
Accepted
点击查看代码 ```cpp
</details>
</div>
posted @   WatPz  阅读(92)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示