寒假算法训练第四次总结

第一题

思路:利用扩展欧几里得算法模板求出x,若b与MOD不互质则无解

点击查看代码
#include<iostream>
#include<string>
using namespace std;
typedef long long ll;
const int MOD = 19260817;
//扩展欧几里得算法
ll extended_gcd(ll a, ll b, ll& x, ll& y) {
    if (b == 0) {
        x = 1;
        y = 0;
        return a;
    }
    ll gcd = extended_gcd(b, a % b, y, x);
    y -= a / b * x;
    return gcd;
}
//求b的逆元
ll inv(ll b, ll mod) {
    ll x, y;
    ll gcd = extended_gcd(b, mod, x, y);
    if (gcd != 1) {
        return -1;
    }
    return (x % mod + mod) % mod;
}
//高精度数对低精度数取模
ll mod(const string& num, ll mod) {
    ll res = 0;
    for (char ch : num) {
        res = (res * 10 + (ch - '0')) % mod;
    }
    return res;
}
int main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);

    string a_str, b_str;
    cin >> a_str >> b_str;
    //取模
    ll a = mod(a_str, MOD);
    ll b = mod(b_str, MOD);
    //求b逆元
    ll b_inv = inv(b, MOD);
    if (b_inv == -1) {//无解
        cout << "Angry!" << endl;
    }else{
        ll x = (a * b_inv) % MOD;
        cout << x << endl;
    }
    return 0;
}

总结:扩展欧几里得算法很有用,还有高精度数对低精度数取模可以用string类储存然后边转数字边取模 第二题

思路:任意两个连续的数字必互质

点击查看代码
#include<bits/stdc++.h>
using namespace std;
int t;
int main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin >> t;//测试数据t组
    int l, r;
    for(int i = 0; i < t; i++) {
        cin >> l >> r;
        int ans = r-l;
        if(l == 1) {
            if(r < 2) {
                ans++;
            }
        }
        cout << ans << endl;
    }
    return 0;
}
总结:记得要加上特判 第三题

思路:直接筛一定会超时,必须要转化,可以先预处理出2sqrt(r)区间的素数,因为r的因子最大不超过sqrt(r),最后用这些素数筛掉lr区间的合数即可

点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 50000;
int l, r;
int main() {
    cin >> l >> r;
    if(l == 1) l = 2;
    //筛出1~max(sqrt(r))之间所有素数
    vector<bool> isPrime(N+1,true);
    vector<int> primes;
    for(int i = 2; i <= N; i++) {
        if(isPrime[i]) {
            primes.push_back(i);
        }
        for(int j = 0; j < primes.size() && i * primes[j] <= N; j++) {
            isPrime[i * primes[j]] = false;//标记合数
            if(i % primes[j] == 0)
                break;
        }
    }
    int len = r - l + 1;
    vector<bool> res(len, true);
    for(int p : primes) {
        if((ll)p * p > r)
            break;
        ll start;

        if(l % p == 0) start = l;
        else start = l + (p - l % p);
        start = max(start, (ll)p * p);

        for(ll j = start; j <= r; j+=p) {
            res[j - l] = false;
        }
    }
    int cnt = 0;
    for(int i = 0; i < len; i++) {
        if(res[i]) cnt++;
    }
    cout << cnt << endl;
    return 0;
}

总结:其实可以直接用埃氏筛筛出l~r的素数,我这里用线性筛纯属多此一举 第四题

思路:首先这样的数对的积是固定为x*y,然后直接按照gcd和lcm的定义枚举就行

点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll x, y;
int main() {
    cin >> x >> y;//x为最大公约数,y为最小公倍数
    if(x == y) {
        cout << 1 << endl;
        return 0;
    }
    ll xy = x * y;
    int cnt = 0;
    for(int i = 1; i * x <= sqrt(xy); i++) {
        if(xy % (i*x) == 0)
            if(__gcd(i*x, xy / (i*x)) == x)
                cnt++;
    }
    cout << 2*cnt << endl;


    return 0;
}
总结:注意特判x == y的情况 第五题

思路:先根据数据范围筛掉那些无用数据,然后再统计各个可能的l值对应有多少个元素的倍数可以是l,最后根据结果再次遍历,反推方案

点击查看代码
#include<iostream>
#include<vector>
#include<algorithm>
const int MAX_M = 1e6+10;
using namespace std;
int n, m;
int main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin >> n >>m;//n个元素,子序列lcm不超过m
    vector<int> a(n);
    for(int i = 0; i < n; i++) cin >> a[i];

    //过滤掉大于m的元素
    vector<int> flitered_a;
    for(int x : a) 
        if(x <= m)
            flitered_a.push_back(x);

    //统计每个元素的频率(利用哈希表)
    vector<int> freq(MAX_M, 0);
    for(int x : flitered_a) 
        freq[x]++;


    //预处理可能的 LCM 值(只能是某些元素的倍数)
    vector<int> lcm_count(MAX_M, 0);
    for (int x = 1; x <= m; x++) {
        if (freq[x] > 0) { //不存在的元素的倍数不必考虑
            for (int multiple = x; multiple <= m; multiple += x) {
                lcm_count[multiple] += freq[x];
            }
        }
    }

    //找到最大子序列长度和对应的 LCM 值
    int max_len = 0;
    int best_lcm = 1;
    for (int l = 1; l <= m; ++l) {
        if (lcm_count[l] > max_len) {
            max_len = lcm_count[l];
            best_lcm = l;
        }
    }
    
    //反推出符合要求的元素
    vector<int> res;
    for(int i = 0; i < n; i++) {
        if(a[i] <= m && best_lcm % a[i] == 0) {
            res.push_back(i+1);
        }
    }

    //输出结果
    cout << best_lcm << ' ' << max_len << endl;
    for(int x : res) {
        cout << x << ' ';
    }



    return 0;
}
总结:又是结果反推方案的思路,跟上次那题 书的复制 思路很象,还有就是要对数据进行预处理 第六题

思路:看学长直播后根据学长提供的思路写的,首先可以明确:2可以生成所有的数,无论奇偶;还有素数无法被生成;生成器必须比生成的数小。因此只有一个素数的话,还有可能合法(以它为生成器的话);但是一旦出现两个素数,就能直接判定数据非法。然后就是先对数组进行从小到大的排序,这样素数的合法出现情况就只能是第一个出现且只出现一次,并且之后所有的数字都可以由该素数表示,如果最小的数字真的是素数的话,之后就是对每个元素进行合法性检查,偶数检查它谁否大于等于该素数的两倍,奇数检查扣掉该奇数的最小质因数后,得到的偶数是否合法。一旦再检测到一个素数就就可以直接判定非法输出

点击查看代码
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
const int N = 4e5+9;
int t;
int func(int n) {//求奇数的最小质因数
    int res = 2;
    while(res++) {
        if(n % res == 0) {
            break;
        }
    }
    return res;
}
int main(){
    cin >> t;

    vector<bool> isPrime(N+1,true);
    vector<int> primes;
    for(int i = 2; i <= N; i++) {
        if(isPrime[i]) primes.push_back(i);
        for(int j = 0; j < primes.size() && i * primes[j] <= N; j++) {
            isPrime[i * primes[j]] = false;
            if(i % primes[j] == 0) break;
        }
    }

    while(t--) {
        int len; cin >> len;
        vector<int> a(len);
        for(int i = 0; i < len; i++) cin >> a[i];
        sort(a.begin(), a.end());

        int pri_cnt = 0;
        int ans = 2;
        for(int i = 0; i < len; i++) { 
            //判断素数
            if(isPrime[a[i]]) {
                pri_cnt++;
                if(pri_cnt == 2) {//两个素数就无解
                    ans = -1;
                    break;
                }
                if(pri_cnt == 1 && i != 0) {//素数不在第一个出现也无解
                    ans = -1;
                    break;
                }
                ans = a[i];//暂定答案为该素数
            }else{//分别对奇偶数进行合法性判断
                if(a[i] % 2 == 0) {//偶数
                    if(pri_cnt == 0) {//如果这之前还没出现过素数
                        continue;
                    }else{//如果出现过素数就判断是否大于等于该素数的两倍
                        if(a[i] >= 2 * ans) {
                            continue;
                        }else{
                            ans = -1;
                            break;
                        }
                    }
                }else{//奇数
                    if(pri_cnt == 0) {
                        continue;
                    }else{
                        int prime_divider = func(a[i]);
                        a[i] -= prime_divider;//先将该奇数减去其最小质因子
                        if(a[i] >= 2 * ans) {//如果得出的偶数能合法
                            continue;
                        }else{//得出的偶数非法,无解
                            ans = -1;
                            break;
                        }
                    }    
                }
            }
        }
        cout << ans << endl;    

    }
    return 0;
}
总结:还得是学长大人,数学思维简直无敌
posted @ 2025-02-10 19:26  Ayanami_Rei  阅读(16)  评论(0)    收藏  举报