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;
}
本文来自博客园,作者:Orange_new,转载请注明原文链接:https://www.cnblogs.com/JPGOJCZX/p/18442374

浙公网安备 33010602011771号