校测 2024 0930 数学
0-30-0,数学还只打了暴力,菜就多练
Problem 1. facsum
省流: $$f(n) =(\sum\limits_{d\mid n}\varphi(d))^m(\sum\limits_{d\mid n}\sigma_0(d)\mu(\frac{n}{d})\frac{n}{d})$$
求 \(\sum\limits_{i = 1}^nf(i)\bmod 1e9+7\)
大概是把 \(\sum\limits_{i=1}^n\) 拆开,打表找规律,分组线性筛
前面的区域以后再来探索吧
Problem 2. group
Mr.Hu 最近在研究等比数列,即形如:
现在,Mr.Hu 想知道,对于给定的非负整数 \(a\),上面这个无穷数列在摸 \(mod\) 意义下有多少项是本质不同
的。(保证 \(gcd(a, mod) = 1\))。
Input
第 1 行一个整数:\(T\) ,表示数据组数。接下来 \(T\) 行,每行两个整数:\(a, mod\)。
Output
对于每组数据,输出一行,包含一个整数,表示模意义下本质不同的数有多少个。
Note
• 对于 30% 的数据,\(0 ≤ a ≤ 1e3,1 ≤ mod ≤ 1e3\) ;
• 对于 100% 的数据,\(0 ≤ a ≤ 2 × 1e9,1 ≤ mod ≤ 2 × 1e9,且保证 gcd(a, mod) = 1,1 ≤ T ≤ 100。\)
分析
对于模意义下的等比数列最小循环节长度,考虑循环节到最后一项必定为 \(1\) , 想到欧拉定理 \(a^{\varphi(n)}\equiv 1 \pmod p\),直接暴力求 \(\varphi(n)\) ,枚举其因子 \(x\) 是否满足 \(a^x\equiv 1 \pmod p\),快速幂优化一下,时间复杂度 \(O(T\sqrt n\log(m))\)
AC代码:
#include<bits/stdc++.h>
using namespace std;
inline int read() {
int f = 1, otto = 0;
char a = getchar();
while(!isdigit(a)) {
if(a == '-') f = -1;
a = getchar();
}
while(isdigit(a)) {
otto = (otto << 1) + (otto << 3) + (a ^ 48);
a = getchar();
}
return f * otto;
}
int a, mod;
int ans = 0;
int getphi(int x) {
int as = x;
for(int i = 2; i * i<= x; i++) {
if(x % i == 0) {
while(x % i == 0) x /= i;
as = as / i * (i - 1) ; //公式
}
}
if(x > 1) as = as / x * (x - 1);
return as;
}
int qpow(long long x, int y) {
long long as = 1;
while(y) {
if(y & 1) (as *= x) %= mod, y--;
(x *= x) %= mod, y >>= 1;
}
return as;
}
void solve() {
if(a == 0) return printf("0\n"), void(0);
int phi = getphi(mod);
ans = phi;
for(int i = 1; i * i <= phi; i++) {
if(phi % i == 0){
if(qpow(a, i) == 1) ans = min(ans, i);
else if(qpow(a, phi / i) == 1) ans = min(ans, phi / i);
}
}
return printf("%lld\n", ans), void(0);
}
int main() {
freopen("group.in", "r", stdin);
freopen("group.out", "w", stdout);
int T = read();
while(T--) {
a = read(), mod = read();
solve();
}
return 0;
}
Problem 3. ccount
Mr.Hu 最近在学习组合数,他觉得这些数非常美丽。
于是,他写下了这样一个数列:
Mr.Hu 想知道,这些数里面,有多少个数是 \(5\) 的倍数。
Input
第 1 行一个整数:\(T\) ,表示数据组数。
接下来 \(T\) 行,每行三个整数:\(l,r,n\)
Output
对于每组数据,输出一行,包含一个整数,表示答案。
Note
• 对于 20% 的数据,\(1 ≤ n ≤ 5000\)。
• 对于 40% 的数据,\(1 ≤ n ≤ 1e9,1 ≤ r − l + 1 ≤ 5000\)。
• 对于 100% 的数据,\(1 ≤ n ≤ 1e18,0 ≤ l ≤ r ≤ n,1 ≤ T ≤ 100\)。
分析
由于 \(l, r\) 长度很大,不能直接做, 考虑转换。求组合数是 \(5\) 的倍数的个数, 即求:
由于 \(5\) 是质数,根据 \(Lucas\) 定理,可以拆成:$$\sum \limits_{i = l}^r [\binom{\lfloor
\frac{n}{5^x} \rfloor}{\lfloor
\frac{i}{5^x}\rfloor}\cdot\binom{\lfloor
\frac{n}{5^{x-1}}\rfloor\bmod5}{\lfloor
\frac{i}{5^{x-1}}\rfloor\bmod5}\cdot...\cdot\binom{\lfloor
\frac{n}{5^{1}}\rfloor\bmod5}{\lfloor
\frac{i}{5^{1}}\rfloor\bmod5}\cdot\binom{
n\bmod5}{
i\bmod5 }\bmod 5=0]$$
即 $$n=(a_1a_2a_3...a_x)_5$$
要使 \(\binom{n}{i}\bmod5=0\) 当且仅当 \(\exists b_j > a_j\) ,考虑在五进制下做数位DP
AC代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
inline ll read() {
ll f = 1, otto = 0;
char a = getchar();
while(!isdigit(a)) {
if(a == '-') f = -1;
a = getchar();
}
while(isdigit(a)) {
otto = (otto << 1) + (otto << 3) + (a ^ 48);
a = getchar();
}
return f * otto;
}
ll dp[30][30], a[30], b[30];
ll l, r, n, lena, lenb, ans;
ll dfs(int pos, int r, int lim) {
if(!lim && dp[pos][r] != -1) return dp[pos][r];
if(pos == 0) return r == 1 ? 1 : 0;
ll ret = 0;
int num = lim ? b[pos] : 4;
for(int i = 0; i <= num; i++) {
ret += dfs(pos - 1, r || (i > a[pos]), lim && i == num);
}
if(!lim) dp[pos][r] = ret;
return ret;
}
void part(ll x) {
lenb = 0;
memset(dp, -1, sizeof dp);
while(x) {
b[++lenb] = x % 5, x /= 5;
}
ans = dfs(lenb, 0, 1);
return;
}
void solve() {
lena = 0;
memset(a, 0, sizeof a);
memset(b, 0, sizeof b);
while(n) a[++lena] = n % 5, n /= 5;
part(r); ll ansr = ans;
if(l) {part(l - 1); ansr -= ans;}
printf("%lld\n", ansr);
return;
}
int main() {
freopen("ccount.in", "r", stdin);
freopen("ccount.out", "w", stdout);
ll T = read();
while(T--) {
l = read(), r = read(), n = read();
solve();
}
return 0;
}