1941 字
10 分钟
数学专题-练习题

题目链接#

数学练习题

编号#

题意#

NN 个数,每个数介于 11MiM_i 之间(可以为 11MiM_i)。对于每一个 MiM_i 选择一个数这个数在 11MiM_i 之间,且选择的数不能重复。问一共有多少选择方案。

题解#

从小到大排序后,假设当前你对前i个进行了选择,这 ii 个选择的范围在 11MiM_i 之间,对于第 Mi+1M_{i+1} 个数它之中一定有 ii 个数已经被选择过了,因为已经从小到大排过序了。所以对于每个 MiM_i,它对答案的贡献是使答案乘上 Mii1M_i - i - 1 的结果。所以当 Mi<i1M_i < i - 1 时是无解的。

代码#

#include<bits/stdc++.h>

using namespace std;

#define int long long
const int mod = 1e9 + 7;
void sl(){
    int n;
    cin >> n;
    vector<int> a(n + 1);
    for(int i = 1 ;i <= n ;i++){
        cin >> a[i];
    }
    sort(a.begin() + 1,a.end());
    int sum = 1;
    for(int i = 1 ;i <= n ;i++){
        if(a[i] < i){
            sum = 0;
            break;
        }
        sum = sum * (a[i] - i + 1) % mod;
    }
    cout << sum << '\n';
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t = 1;
    // cin >> t;
    while(t--) sl();
    return 0;
}

小车问题#

题意#

ABA B 两地的距离 ss,初始两人在 AA 地,人的步行速度 aa 和车的速度 bb,车一次只能载一人求两人同时到达 BB 的所花费的最少时间。保证车速大于人的速度。

题解#

假设先开车带一个人行驶了 xx 距离,然后折返回去接第二个人,一路开到 BB 点,且保证两人同时到达,由此可列出方程:

(sx)/a=2(2x/(a+b)x/b)+(sx)/bx=(b+a)s/(b+3a)(s - x) / a = 2 * (2 * x / (a + b) - x / b) + (s - x) / b \\ x = (b + a) * s / (b + 3 * a)

根据 xx 可求出最后时间为:x/b+(sx)/ax / b + (s - x) / a

代码#

#include<bits/stdc++.h>

using namespace std;

#define int long long

void sl(){
    double s,a,b;
    cin >> s >> a >> b;
    double x = (b + a) * s / (b + 3 * a);
    double t = x / b + (s - x) / a;
    cout << fixed << setprecision(6) << t << '\n';
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t = 1;
    // cin >> t;
    while(t--) sl();
    return 0;
}

小凯的数字#

题意#

QQ 次询问,每次询问给一个 llrr,求出

l(l+1)(l+2)...(r1)rmod9\overline{l(l+1)(l+2)...(r-1)r}\mod 9

后的结果。

题解#

首先观察数据范围,发现询问次数在 10410^4 次,每次的询问范围在 101210^{12}。因此可以判断该题应该是一道 O(1)O(1) 的结论题。

首先观察 7mod97 \mod 970mod970 \mod 9700mod9700 \mod 9 等等,发现结果都是 77。因此可以大胆假设对于任意整数 xx,有 xmod9=(x×10y)mod9x \mod 9 = (x \times 10^y) \mod 9(其中 yy00 到正无穷之间的整数)。

而对于 llrr 拼接起来形成的巨大数,可以看做 l×10yl+(l+1)×10yl+1++r×10yrl \times 10^{y_l} + (l + 1) \times 10^{y_{l + 1}} + \ldots + r \times 10^{y_r}。根据之前的假设,将 1010 的上标全部置为 00xmod9x\mod 9 意义下的结果是一样的,所以原式变为 l+(l+1)++r=(rl+1)×(l+r)2l + (l + 1) + \ldots + r = \frac{(r - l + 1) \times (l + r)}{2}

因为我们最后需要对答案取模,为了防止取模后奇偶性发生改变,我们需要对 (rl+1)(r - l + 1) 或者 (l+r)(l + r) 单独除 22

代码#

#include<bits/stdc++.h>

using namespace std;

#define int long long
const int mod = 9;

void sl(){
    int l,r;
    cin >> l >> r;
    int sum;
    if((l + r) % 2 == 0){
        sum = (((l + r) / 2 % mod) * ((r - l + 1) % mod)) % mod;
    }else{
        sum = (((l + r) % mod) * ((r - l + 1) / 2 % mod)) % mod;
    }
    cout << sum << '\n';
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t = 1;
    cin >> t;
    while(t--) sl();
    return 0;
}

平均数#

题意#

给一个长度为 nn 的数列,我们需要找出该数列的一个连续子串,使得该子串平均数最大化,并且子串长度 m\ge m

题解#

我们使用二分答案来解决这个问题。对于当前二分出的平均值,我们要确定它的合法性,可以通过以下步骤实现:

  1. 将所有数先减去平均值。
  2. 这时我们相当于是要找到一段区间,使得它的和大于 00
  3. 对减后的数组进行前缀和计算。
  4. 假设我们当前找到了 pre[i],只需要判断 pre[1]pre[i - m] 之间的最小值是否小于 pre[i],就可以判断出是否存在这样的区间。

代码#

#include<bits/stdc++.h>

using namespace std;

#define int long long
void sl(){
    int n,m;
    cin >> n >> m;
    vector<int> a(n + 1);
    for(int i = 1 ;i <= n ;i ++){
        cin >> a[i];
        a[i] *= 10000;
    }
    auto check=[&](int x){
        vector<int> b = a,pre(n + 1);
        int mn = 0;
        for(int i = 1 ;i <= n ;i++){
            b[i] -= x;
            pre[i] = pre[i - 1] + b[i];
            if(i >= m){
                mn = min(mn,pre[i - m]);
                if(pre[i] > mn) return 1;
            }
        }
        return 0;
    };
    int l = 0,r = 30000000;
    while(l <= r){
        int mid = (l + r) >> 1;
        if(check(mid)) l = mid + 1;
        else r = mid - 1;
    }
    cout << l / 10 << '\n';
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t = 1;
    // cin >> t;
    while(t--) sl();
    return 0;
}

Modified GCD#

题意#

qq 组询问,每次询问 a,ba,b[l,r][l,r] 之间的最大公约数,如果不存在输出 1-1

题解#

首先考虑 a,ba, b 的最大公约数 gcd(a,b)\gcd(a, b)a,ba, b 其他公约数之间的关系,可以发现 a,ba, b 的所有公约数均是最大公约数的因数。因此我们先处理出最大公约数的所有因数,然后对于每次询问的 l,rl, r 进行二分查找即可。

代码#

#include <bits/stdc++.h>

using namespace std;
#define int long long

void sl( ){
    int a,b,q;
    cin >> a >> b >> q;
    int gd = __gcd(a,b);
    set<int> p;
    for(int i = 1 ;i * i <= gd ;i ++){
        if(gd % i == 0){
            p.insert(i);
            p.insert(gd / i);
        }
    }
    while(q--){
        int l,r;
        cin >> l >> r;
        if(gd < l){
            cout << "-1\n";
            continue;
        }
        if(gd <= r){
            cout << gd << '\n';
            continue;
        }
        auto it = p.upper_bound(r);
        it--;
        if(*it < l) cout << "-1\n";
        else cout << *it << '\n';
    }
}

signed main( ){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t = 1;
    //	cin>>t;
    while(t--) sl();
    return 0;
}

细胞分裂#

题意#

给你 m1m_1m2m_2,并且给你 nn 个数,假设第 ii 个数为 SiS_i,找到最小的 kk 使得,Sikmodm1m2=0S_i ^ {k} \mod m_1^{m_2} = 0,如果不存在这样的 kk,输出 1-1

题解#

首先我们需要知道两个结论:

  1. xmody=0x \mod y = 0,将 xxyy 质因数分解后,s1s1xx 的质因数集,s2s2yy 的质因数集,那么 s2s2s1s1 的一个子集。
  2. 假设 zzxx 的一个质因数,xx 可以分解出 numznum_zzz,而 xyx^y 可以分解出 numz×ynum_z \times yzz

之后我们在回来看题目,要保证 Sikmodm1m2=0S_i^k \mod m_1^{m_2} = 0,可以先对 m1m_1 进行质因数分解,并且对于每一个 SiS_i 同样进行质因数分解。如果 SiS_i 可以通过幂次操作使得取模结果为 0,那么 m1m_1 的每一个质因数也需要在 SiS_i 中出现,并且操作之后的每一个质因数的数量也要大于 m1m_1 的每一个质因数的数量乘上 m2m_2

代码#

#include <bits/stdc++.h>

using namespace std;
#define int long long
const int N = 3e4 + 10;

vector<int> prime;
bool is[N];

void init(){
	for(int i = 2 ;i < N ;i ++){
		if(!is[i]){
			prime.push_back(i);
	    	for(int j = i * i ;j < N ;j += i){
				is[j] = 1;
			}
		}
	}
}

void sl( ){
	int m1,m2,n;
	cin >> n >> m1 >> m2;
	vector<int> a(n + 1);
	map<int,int> mp;
	int idx = 0;
	while(m1 > 1){
		while(m1 % prime[idx] == 0){
			mp[prime[idx]] ++;
			m1 /= prime[idx];
		}
		idx++;
	}
	for(auto it : mp){
		auto[u,v] = it;
		mp[u] *= m2;
	}
	int f = 0,num = -1;
	for(int i = 1 ;i <= n ;i ++){
		cin >> a[i];
	}
	for(int i = 1 ;i <= n ;i++){
		int flag = 1,mn = 0;
		for(auto x : mp){
			if(a[i] % x.first != 0){
				flag = 0;
				break;
			}
			int ans = 0;
			while(a[i] % x.first == 0){
				a[i] /= x.first;
				ans++;
			}
			mn = max(mn,(x.second - 1) / ans + 1);
		}
		if(flag == 0) continue;
		if(num == -1){
			num = mn;
		}else{
			num = min(num,mn);
		}
	}
	cout << num << '\n';
}


signed main( ){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	init();
	int t = 1;
//	cin>>t;
	while(t--) sl();
	return 0;
}
数学专题-练习题
https://fuyuki.fun/posts/题解/数学专题-练习题/
作者
Fuyuki_Vila
发布于
2025-01-18
许可协议
CC BY-NC-SA 4.0