2025.5.28

刷题日记
今天solve了四道题
相较前几天狠狠的刷1600/1700来说,今天的强度是低了点:一道1100,一道1300,一道1400,一道暂未评定
但是今天的这四道题都是独立解决的,相较于之前必须学一上午一下午题解才能勉强过关的题来说,并非一无是处吧(



Codeforces Round 1027 (Div.3) F. Small Operations

对于给定的x,y,k,要求使用两种操作将x转化为y,并最小化操作次数
操作1:从 [1, k] 中选取一个数字a,执行 x *= a
操作2:从 [1, k] 中选取一个数字a,执行 x /= a
从题中我们不难发现,x * (y/x) == y,那么我们只需要去凑一个分子为y,分母为x的分数即可
如果每次都从 [1, k] 的区间中选择数字去凑,那么显然是不好凑的。此时我们考虑倒推
那么这道题的思路就是:将分数 y/x 用尽可能小的次数化为 1/1,并且每次只能从 [1, k] 中选取数字去进行除法操作
我们可以采用bfs的方法去计算出该过程所需最小的次数,代码如下:
注意:此处bfs内的前两行判断非常重要,会影响答案的正确性以及很大程度上影响代码运行的时间!

点击查看代码
#include <bits/stdc++.h>

using ll = int64_t;
const ll N = 2e5 + 10;
const ll INF = LLONG_MAX;
const ll mod = 998244353;

void solve() {
    int x, y, k;
    std::cin >> x >> y >> k;

    int tmp = std::__gcd(x, y);
    int fz = y / tmp;
    int fm = x / tmp;

    if (fz == 1 && fm == 1) {
        std::cout << "0\n";
        return;
    }
    
    std::function<int(int)> bfs = [&] (int start) {
        if (start == 1) return 0;
        if (start <= k) return 1;

        typedef struct node { int num, time; } node;
        std::queue<node> q;
        std::unordered_set<int> st;

        q.push({start, 0});
        st.insert(start);
        
        while (q.size()) {
            auto [now, step] = q.front();
            q.pop();
            
            for (int i = k; i >= 2; i--) {
                if (now % i == 0) {
                    int next = now / i;
                    if (next == 1) return step + 1;

                    if (!st.count(next)) {
                        st.insert(next);
                        q.push({next, step + 1});
                    }
                }
            }
        }
        return -1;
    };
    
    int cnt_fz = bfs(fz);
    int cnt_fm = bfs(fm);
    
    if (cnt_fz != -1 && cnt_fm != -1) {
        std::cout << cnt_fz + cnt_fm << '\n';
    } else {
        std::cout << "-1\n";
    }
}

int main () {   
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int t = 1;
    std::cin >> t;

    while (t--) {
        solve();
    }
    
    return 0;
}


Educational Codeforces Round 177 (Rated for Div. 2) C. Disappearing Permutation

给定两个长度为n的序列p和d,接下来进行n次操作,每次将p[ d[ i ] ]置为0,问经过多少次操作可以将p重新变为一个排列
有且仅有一种操作:将p[ i ]置为i
这道题困扰了我好久来着,挺早之前就尝试去写这道题,但一直写不出来,看题解也看不懂
现在重新拾起了这道题,(正好题解也忘完了),没想到一次就过了
通过分析我们可以知道,每次删数字的时候,需要进行的操作数和该数字以及索引构成的环的大小有关
举个例子,索引为1的值为4,索引为4的值为1,那么这两个数字就构成了一个大小为2的环
显然此环之外的其他部分是不受影响的,而这个环上的所有数值都需要修改才可以让p重新构成排列
我们可以在每次修改后跑一个小小的dfs去搜索,记录环的大小,并且把每个点都标记为已访问,这样后续的搜索就不会把重复的给跑了
虽然是dfs,但是实际上是一个链式的搜索,总复杂度取决于点的个数,为O(n)

点击查看代码
#include <bits/stdc++.h>

using ll = int64_t;
const ll N = 2e5 + 10;
const ll INF = LLONG_MAX;
const ll mod = 998244353;

void solve () {
    int n;
    std::cin >> n;

    std::vector<int> p(n + 1), d(n + 1);
    std::vector<int> vis(n + 1, 0);

    for (int i = 1; i <= n; i++) {
        std::cin >> p[i];
    }
    for (int i = 1; i <= n; i++) {
        std::cin >> d[i];
    }

    std::function<int(int, int, int)> dfs = [&] (int bg, int now, int size) {
        if (now == bg && size != 1) {
            return size - 1;
        }
        else {
            vis[now] = 1;
            return dfs(bg, p[now], size + 1);
        }
    };

    int ans = 0;
    for (int i = 1; i <= n; i++) {
        int bg = p[d[i]];
        if (!vis[bg]) {
            ans += dfs(bg, bg, 1);
        }
        std::cout << ans << " \n"[i == n];
    }
}

int main () {   
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int t = 1;
    std::cin >> t;

    while (t--) {
        solve();
    }
    
    return 0;
}


Educational Codeforces Round 178 (Rated for Div. 2) D. Array and GCD

这道题的意思是,如果可以通过任意次的“选中任意(i, j),使得a[i]--, a[j]++”这一操作,使得整个数组中任意两数互质,那么这个数组就是好的
现在给定一个数组,要求你判断是不是好数组;如果不是,你可以删除数组中的任意元素让它变成好数组,要求输出最小化的操作次数
我们可以考虑贪心的策略:先不考虑这些数啊啥的都是几,咱们先把他们的和sum求出来,假定他们都为0,然后在所有质数中从小往大依次放就可以了
可以证明这样是最优解,毕竟任意两个质数肯定互质,从小往大依次放可以保证删的数字最小化
在这样的思路下,我们需要使用到线性筛去预处理一下范围内所有质数了
两种情况:

  1. 当我sum减到最后还满足sum >= 0,那么这个数组就是好的,输出0
  2. 当sum减到最后变成负数了,那么我们就要考虑删数字了;显然我们这里希望sum尽可能大,所以每次我们让sum减去原数组中最小的数字,然后再补上一个目前最大的质数

代码如下:

点击查看代码
#include <bits/stdc++.h>

using ll = int64_t;
const ll N = 1e7 + 10;
const ll INF = LLONG_MAX;
const ll mod = 998244353;

int prim[N], now;
bool vis[N];
std::unordered_map<int, int> st;

void init () {
    for (int i = 2; i <= N; i++) {
        if (!vis[i]) {
            prim[++now] = i;
            st[i] = 1;
        }

        for (int j = 1; j <= now && prim[j] * i <= N; j++) {
            vis[prim[j] * i] = 1;
            if (i % prim[j] == 0) break;
        }
    }
}

void solve () {
    int n;
    std::cin >> n;

    std::vector<ll> a(n + 1);
    std::priority_queue<ll, std::vector<ll>, std::greater<ll>> q;

    ll sum = 0;
    for (int i = 1; i <= n; i++) {
        std::cin >> a[i];
        q.push(a[i]);
        sum += a[i];
    }

    if (n == 1) {
        std::cout << "0\n";
        return;
    }

    int cnt = 0, id = 1;
    while (cnt < n) {
        sum -= prim[id];
        id++, cnt++;
    }
    id--;

    if (sum >= 0) {
        std::cout << "0\n";
    } 
    else {
        int ans = 0;
        while (q.size()) {
            auto now = q.top();
            q.pop();

            sum -= now;
            sum += prim[id];
            id--;
            ans++;

            if (sum >= 0) break;
            else continue;
        }
        
        if (ans >= n) 
            std::cout << n - 1 << '\n';
        else 
            std::cout << ans << '\n';
    }
}

int main () {   
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    init();

    int t = 1;
    std::cin >> t;

    while (t--) {
        solve();
    }
    
    return 0;
}

posted @ 2025-05-28 23:00  _彩云归  阅读(23)  评论(0)    收藏  举报