2024.9.29校测

T1

题目描述

\(Mr.Hu\) 最近偶得一函数:

\(f(n) = (\displaystyle\sum_{d \mid n} \varphi(d))^m (\sum_{d \mid n} \sigma_0(d) \mu(\frac nd) \frac nd)\)

其中 \(\sigma_0(n)\) 表示 \(n\) 的正约数个数,比如 \(\sigma_0(12) = 6\),因为 \(12\)\(1, 2, 3, 4, 6, 12\)\(6\) 个正约数。

其中 \(\varphi(n)\) 是欧拉函数,\(\mu(n)\) 是莫比乌斯函数。

又有:\(F(n) = \displaystyle\sum_{i=1}^nf(i)\)

\(Mr.Hu\) 希望你计算 \(F(n) \bmod 10^9 + 7\) 的值。

输入格式

第一行包含两个整数:\(n, m\)

输出格式

输出一行包含一个数,表示答案。

输入样例

3 1

输出样例

1000000005

样例解释

\(f(1) = 1, f(2) = 0, f(3) = −3\),故 \(F(3) = f(1) + f(2) + f(3) = −2\),在模意义下,这个数为:\(1000000005\)

数据规模

对于 \(20\%\) 数据,\(1 \leq n \leq 5000\)

对于 \(50\%\) 数据,\(1 \leq n \leq 10^5\)

对于 \(100\%\) 数据,\(1 \leq n \leq 10^7,1 \leq m \leq 10\)

题解

首先根据欧拉反演 (具体可见数论学习笔记(一)(2024.7.25)),\(\displaystyle\sum_{d \mid n} \varphi(d) = n\),因此原式 \(= \displaystyle\sum_{i=1}^n i^m \sum_{d \mid i} \sigma_0(d) \mu(\frac id) \frac id\)

考虑改变枚举顺序,则原式 \(= \displaystyle\sum_{i=1}^n \sum_{d \mid i} i^m \sigma_0(d) \mu(\frac id) \frac id\),考虑一个简单的 trick,枚举 \(1\)\(n\) 中所有数的因子,相当于枚举两个因子并保证它们的乘积在 \(1\)\(n\) 之间,那么原式就相当于 \(\displaystyle\sum_{d=1}^n \sum_{k=1}^{\lfloor \frac nd \rfloor} (dk)^m \sigma_0(d) \mu(k) k\),此时就再次改变枚举顺序为 \(\displaystyle\sum_{d=1}^n d^m \sigma_0(d) \sum_{k=1}^{\lfloor \frac nd \rfloor}\mu(k)k\)

现在就可以通过线性筛筛出 \(1\)\(n\) 范围内的 \(\sigma_0\) 函数 与 \(\mu\) 函数 (具体可见数论学习笔记(二)(2024.8.16)),进一步预处理出 \(\displaystyle\sum_{k=1}^{\lfloor \frac nd \rfloor}\mu(k)k\),那么这道题就做完了。

完整代码

#include<bits/stdc++.h>
using namespace std;
const int PP = 1e7 + 9, MOD = 1e9 + 7;
int prime[PP], h[PP], sum[PP], isPrime[PP], p_cnt;
short mu[PP], g[PP];
int n, m, ans;
int qpow(int a, int b){
	int ret = 1;
	while(b > 0){
		if(b & 1)
			ret = 1ll * ret * a %  MOD;
		a = 1ll * a * a % MOD;
		b >>= 1;
	}
	return ret;
}
void getPrime(int n){
	mu[1] = h[1] = 1;
	for(int i = 2; i < n; i++){
		if(!isPrime[i]) {
			prime[++p_cnt] = i;
			g[i] = 1;
			h[i] = 2;
			mu[i] = -1;
		}
		for(int j = 1; j <= p_cnt && i * prime[j] < n; j++){
			int tmp = prime[j];
			isPrime[i * tmp] = tmp;
			if(i % tmp == 0) {
				g[i * tmp] = g[i] + 1;
				h[i * tmp] = h[i] / (g[i] + 1) * (g[i] + 2);
				mu[i * tmp] = 0;
				break;
			}
			g[i * tmp] = g[tmp];
			h[i * tmp] = h[i] * h[tmp];
			mu[i * tmp] = -mu[i];
		}
	}
	for(int i = 1; i <= n; i++)
		sum[i] = (sum[i - 1] + qpow(i, m + 1) * mu[i]) % MOD;
}
int main(){
	freopen("facsum.in", "r", stdin);
	freopen("facsum.out", "w", stdout);
	scanf("%d%d", &n, &m);
	getPrime(n + 1);
	for(int i = 1; i <= n; i++){
		int now = qpow(i, m);
		now = (1ll * now * h[i] % MOD * sum[n / i]) % MOD;
		ans = ((ans + now) % MOD + MOD) % MOD;
	}
	printf("%d\n", ans);
	return 0;
}

T2

题目描述

\(Mr.Hu\) 最近在研究等比数列,即形如:\(a, a^2, a^3, \dots, a^n, \dots\)

现在,\(Mr.Hu\) 想知道,对于给定的非负整数 \(a\),上面这个无穷数列在摸 \(mod\) 意义下有多少项是本质不同
的。(保证 \(\gcd(a, mod) = 1\))。

输入格式

\(1\) 行一个整数:\(T\),表示数据组数。

接下来 \(T\) 行,每行两个整数:\(a, mod\)

输出格式

对于每组数据,输出一行,包含一个整数,表示模意义下本质不同的数有多少个。

输入样例

2
1 3
2 5

输出样例

1
4

样例解释

对于第一组数据,数列是:\(1, 1, 1, \dots, 1, \dots\)

对于第二组数据,数列(取模以后)是:\(2, 4, 3, 1, 2, 4, 3, 1, \dots\),总共有 \(4\) 个本质不同的数。

数据规模

对于 \(30\%\) 数据,\(0 \leq a \leq 10^3, 1 \leq mod \leq 10^3\)

对于 \(100\%\) 数据,\(0 \leq a \leq 2 \times 10^9, 1 \leq mod \leq 2 \times 10^9\),且保证 \(\gcd(a, mod) = 1, 1 \leq T \leq 100\)

题解

在这里介绍一下 \(BSGS\) 算法。

\(BSGS\) 是在 \(x, z, p\) 已知的情况下,用来求解 \(x^y \equiv z \pmod p\)\(y\) 的最小值的。

我们考虑将 \(y\) 除以 \(\lceil \sqrt p \rceil\),可以得到带余的式子 \(y = a \lceil \sqrt p \rceil + b(b < \lceil \sqrt p \rceil)\),那么这个式子也可以写成 \(y = (a + 1)\lceil \sqrt p \rceil) - (\lceil \sqrt p \rceil - b)\)

现在用 \(c\) 代替 \(a + 1\)\(d\) 代替 \(\lceil \sqrt p \rceil - b\),那么 \(y = c\lceil \sqrt p \rceil - d\),那么一开始的同余式就等价于 \(x^{c\lceil \sqrt p \rceil} \equiv z \times x^d \pmod p\)

由于 \(x^0 \equiv x^{\varphi(p)} \equiv 1 \pmod p\),此处会形成一个循环节,那么 \(y\) 的最小值一定小于 \(\varphi(p)\),而 \(\varphi(p)\) 又一定小于 \(p\),因此 \(c\)\(d\) 都在 \(\sqrt p\) 级别。

那么就可以枚举同余式右边的 \(d\),将算出来的值存入一个哈希表,再枚举同余式左边的 \(c\),如果查到哈希表中有 \(x^{c\lceil \sqrt p \rceil}\),那么就找到了答案,总时间复杂度为 \(O(\sqrt p)\)

考虑本题如果出现 \(a^n \equiv 1 \pmod p\),那么就找到了这个循环节,也就找到了本质不同的数的个数,因此本题解出上述的方程即可。

完整代码

#include <bits/stdc++.h>
using namespace std;
#define int long long
int BSGS(int a, int b, int p){
	int k = sqrt(p) + 1;
	unordered_map <int, int> Hash;
	for(int i = 0, j = b % p; i < k; i++){
		Hash[j] = i;
		j = j * a % p;
	}
	int ak = 1;
	for(int i = 0; i < k; i++)
		ak = ak * a % p;
	for(int i = 1, j = ak; i <= k; i++){
		if(Hash.count(j))
			return i * k - Hash[j];
		j = j * ak % p;
	}
	exit(0);
}
int a, b, p, T;
signed main(){
	freopen("group.in", "r", stdin);
	freopen("group.out", "w", stdout);
	scanf("%lld", &T);
	while(T--){
		scanf("%lld%lld", &a, &p);
		printf("%lld\n", BSGS(a, 1, p));
	}
	return 0;
}

T3

题目描述

\(Mr.Hu\) 最近在学习组合数,他觉得这些数非常美丽。
于是,他写下了这样一个数:

\(\displaystyle\binom nl, \binom{n}{l + 1}, \binom{n}{l + 2}, \dots, \binom{n}{r - 1}, \binom nr\)

\(Mr.Hu\) 想知道,这些数里面,有多少个数是 \(5\) 的倍数。

输入格式

\(1\) 行一个整数:\(T\),表示数据组数。

接下来 \(T\) 行,每行三个整数:\(l, r, n\)

输出格式

对于每组数据,输出一行,包含一个整数,表示答案。

输入样例

2
1 3 4
1 4 5

输出样例

0
4

样例解释

对于第一组数据,数列是:\(4, 6, 4\),没有 \(5\) 的倍数,故答案为 \(0\)

对于第二组数据,数列是:\(5, 10, 10, 5\),有 \(4\) 个数是 \(5\) 的倍数,故答案为 \(4\)

数据规模

对于 \(20\%\) 的数据,\(1 \leq n \leq 5000\)

对于 \(40\%\) 的数据,\(1 \leq n \leq 10^9, 1 \leq r − l + 1 \leq 5000\)

对于 \(100\%\) 的数据,\(1 \leq n \leq 10^{18},0 \leq l \leq r \leq n, 1 \leq T \leq 100\)

题解

首先根据 Lucas 定理,\(\displaystyle\binom nm \equiv \binom{\lfloor \displaystyle\frac np \rfloor}{\lfloor \displaystyle\frac mp \rfloor} \binom{n \bmod p}{m \bmod p}\pmod p\),那么一直推下去会推出 \(\displaystyle\binom nm \equiv \binom{n_1}{m_1} \binom{n_2}{m_2} \dots \binom{n_k}{m_k} \pmod p(n_i, m_i < p)\)

如果要让这个组合数等于 \(0\) (等价于原式是 \(5\) 的倍数),那么变换后的式子中某一项就等于 \(0\),就相当于至少存在一个 \(m_i\) 大于 \(n_i\),如果把所有 \(n_i\) 从左往右拼成一个数 \(N\),把所有 \(m_i\) 从左往右拼成一个数 \(M\),那么就相当于 \(M\) 中有一位大于 \(N\) 中同一位,考虑到 \(M\)\(N\) 每个数位上的数都小于 \(5\),可以对其做数位 DP。

完整代码

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 39;
int f[N][2] ,digl[N], digr[N], dign[N];
int DFS_l(int pos, int fl){
	if(pos == -1)
		return 1;
	if(f[pos][fl] != -1)
		return f[pos][fl];
	f[pos][fl] = 0;
	int ed = min((fl ? digl[pos] : 4), dign[pos]);
	for(int i = 0; i <= ed; i++)
		f[pos][fl] += DFS_l(pos - 1, fl && i == digl[pos]);
	return f[pos][fl];
}
int DFS_r(int pos, int fl){
	if(pos == - 1)
		return 1;
	if(f[pos][fl] != -1)
		return f[pos][fl];
	f[pos][fl] = 0;
	int ed = min((fl ? digr[pos] : 4), dign[pos]);
	for(int i = 0; i <= ed; i++)
		f[pos][fl] += DFS_r(pos - 1, fl && i == digr[pos]);
	return f[pos][fl];
}
signed main(){
	freopen("ccount.in", "r", stdin);
	freopen("ccount.out", "w", stdout);
	int t;
	scanf("%d", &t);
	while(t--){
		int l, r, n;
		scanf("%lld%lld%lld", &l, &r, &n);
		int L = l, R = r;
		l--;
		for(int i = 0; i < 30; i++){
			digl[i] = l % 5;
			digr[i] = r % 5;
			dign[i] = n % 5;
			l /= 5;
			r /= 5;
			n /= 5;
		}
		memset(f, -1, sizeof(f));
		int ansr = DFS_r(29, 1);
		memset(f, -1, sizeof(f));
		int ansl = DFS_l(29, 1);
		printf("%lld\n", (R - L + 1) - (ansr - ansl));
	}
	return 0;
}
posted @ 2024-09-30 20:00  Orange_new  阅读(24)  评论(0)    收藏  举报