2024 CCPC 郑州邀请赛暨河南省赛 题解

A - Once In My Life (构造)

分析

根据题目要求需要两个d,并且要保证123456789都存在,那么就可以尝试先构造出一个数保证存在123456789。
尝试以下构造方式:设n的长度为len,则可以构造出kn=(12345678910len+1n+1)n, 可以发现kn的前缀一定是1234567890X,此时只要对k每次+1,至多一百次一定能出现想要的d

代码实现

#include <bits/stdc++.h>
int main() {
    std::cin.tie(nullptr)->sync_with_stdio(false);
    #define int long long
    constexpr int N = 123456789LL;
    int tt;
    for (std::cin >> tt; tt--;) {
        int n, d;
        std::cin >> n >> d;
        int k = 123456789LL * std::pow<int>(10, 2 + (int)std::log10<int>(n)) / n + 1;
        auto check = [&](int w) {
            std::vector<int> c(10);
            int num = w * n;
            while (num) c[num % 10] += 1, num /= 10;
            for (int i = 1; i <= 9; ++i) if (c[i] < 1) {
                return false;
            }
            return c[d] >= 2;
        };
        while (!check(k)) k += 1;
        std::cout << k << '\n';
    }
}

B - 扫雷 1

分析

队友写的,并没有看题,就贴一下队友代码和思路:
贪心。要使得买的探测仪最多,就要尽可能的买价格较便宜的,相同便宜的要买后面出现的,因为这样手中的钱会更多。所以我们只需要把价格作为第一关键词升序排序,出现序号作为第二关键词降序排序,然后一个一个尽可能的多买,直到已经到最后一个时退出就行

代码实现

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int main() {
    cin.tie(nullptr)->sync_with_stdio(false);
    
    int n;
    cin >> n;
    vector<tuple<ll , ll>> tl;
    for (int i=1; i<=n; ++ i) {
        int x;
        cin >> x;
        tl.emplace_back(x , -i);
    }
    sort(tl.begin() , tl.end());
    ll ans = 0 , cur = 0 , pre = 0;
    for (auto [a , b] : tl) {
        b = -b;
        if (b >= a && b > pre) {
            ans += (b - cur) / a;
            cur += (b - cur) / a * a;
            pre = b;
        }
        if (b == n) break;
    }
    std::cout << ans << "\n";
}

C - 中二病也要打比赛 (树状数组+dp)

分析

依旧队友神力

代码实现

#include <bits/stdc++.h>
using namespace std;
template <typename T>
struct Fenwick {
    int n;
    vector<T> d;
    Fenwick() : n(0) {}
    Fenwick(int n) : n(n), d(n) {}
    void modify(int x, T v) {
        assert(0 <= x && x < n);
        x += 1;
        while (x <= n) {
            d[x - 1] = max(d[x - 1], v);
            x += x & -x;
        }
    }
    T get(int x) {
        assert(x <= n);
        T v = 0;
        while (x > 0) {
            v = max(v, d[x - 1]);
            x -= x & -x;
        }
        return v;
    }
    T get(int l, int r) {
        assert(l <= r);
        return get(r) - get(l);
    }
    int min_right(T k) {
        int r = 0;
        for (int e = __lg(n); ~e; --e) {
            int u = (1 << e);
            if (r + u <= n && d[r + u - 1] < k) {
                k -= d[(r += u) - 1];
            }
        }
        return r;
    }
};
int main() {
  ios::sync_with_stdio(false);
  cin.tie(nullptr);
  #define int long long
  int n;
  cin >> n;
  vector<int> a(n);
  for (int i = 0; i < n; ++i) {
    cin >> a[i];
    --a[i];
  }
  vector<int> l(n, n);
  vector<int> r(n, 0);
  for (int i = 0; i < n; ++i) {
    l[a[i]] = min(l[a[i]], i);
    r[a[i]] = max(r[a[i]], i);
  }
  for (int i = n - 1; i >= 0; ) {
    assert(i == r[a[i]]);
    int j = l[a[i]], k = i;
    while (k >= j) {
      r[a[k]] = i;
      j = min(j, l[a[k]]);
      --k;
    }
    i = k;
  }
  Fenwick<int> fen(n);
  vector<int> dp(n);
  for (int i = 0; i < n; ++i) {
    assert(i == l[a[i]]);
    for (int j = i; j <= r[a[i]]; ++j) {
      if (j == l[a[j]]) {
        dp[l[a[j]]] = fen.get(a[j]) + 1;
      }
    }
    for (int j = i; j <= r[a[i]]; ++j) {
      if (j == l[a[j]]) {
        fen.modify(a[j], dp[l[a[j]]]);
      }
    }
    i = r[a[i]];
  }
  cout << set<int>(a.begin(), a.end()).size() - *max_element(dp.begin(), dp.end()) << "\n";
}

D - 距离之比(推公式+贪心)

分析

x=x1x2,y=y1y2,对原公式进行平方并化简可得:1+1xy+xy。想让这个式子尽可能的大,也就是说让xy+xy尽量取小。设tanθ=yx, 此时式子变化为tanθ+1tanθ, 可以发现其在θ45135时取到最优。因此只要对x+yxy分别进行排序,此时两两相邻为最优,两者取个最大值即可。

代码实现

#include <bits/stdc++.h>
int main() {
    std::cin.tie(nullptr)->sync_with_stdio(false);
    std::cout << std::fixed << " " << std::setprecision(20);
    #define int long long
    int tt;
    for (std::cin >> tt; tt--;) {
        int n;
        std::cin >> n;
        std::vector<std::pair<int, int>> p(n);
        for (auto &[x, y] : p) {
            std::cin >> x >> y;
        }
        std::sort(p.begin(), p.end(), 
            [&](const auto& a, const auto& b) {
                return a.first + a.second < b.first + b.second;
            });
        double ans = 0;
        for (int i = 1; i < n; ++i) {
            int d1 = p[i - 1].first - p[i].first, d2 = p[i - 1].second - p[i].second;
            ans = std::max<double>(ans, ((abs(d1) + abs(d2)) * 1. / sqrt(d1 * d1 + d2 * d2)));
        }
        std::sort(p.begin(), p.end(), 
            [&](const auto& a, const auto& b) {
                return a.first - a.second < b.first - b.second;
            });
        for (int i = 1; i < n; ++i) {
            int d1 = p[i - 1].first - p[i].first, d2 = p[i - 1].second - p[i].second;
            ans = std::max<double>(ans, ((abs(d1) + abs(d2)) * 1. / sqrt(d1 * d1 + d2 * d2)));
        }
        std::cout << ans << '\n';
    }
}

E - 保卫城邦

分析

太菜了,不会写

F - 优秀字符串 (模拟)

分析

按照题意模拟即可

代码实现

#include <bits/stdc++.h>
int main() {
    std::cin.tie(nullptr)->sync_with_stdio(false);
    int n;
    std::cin >> n;
    int ans = 0;
    for (int i = 0; i < n; ++i) {
        std::string s;
        std::cin >> s;
        if (size(s) == 5 && s[2] == s[4] && std::set<char>{s[0], s[1], s[2], s[3]}.size() == 4) {
            ans += 1;
        }
    }
    std::cout << ans << '\n';
}

G - 扫雷 2 (构造)

分析

原来扫雷2的2是这个2щ(ಠ益ಠщ)
想了有两种构造方法:

第一种方法:
对于n2m时,我们可以直接按照如下方法构造
n=5,m=5   n=5,m=6
01100        01100
11000        11000
10000        10000
00000        00000
00000        00001
此时对于m为奇数直接在左上角构造两个斜杠,为偶数时多出来的可以直接放在右下角。(注意特判n=5m=10的情况)
此时对于n>2m的情况我们考虑缩小矩阵,可将雷埋在最下面一行和最右边一行,此时可以得到n1的矩阵,例如 n=6,m=25时可以构造如下矩阵:
011011
110011
100011
000011
111111
111111
此时问题回到n2m的情况,注意构造过程中的细节即可。

第二种方法(感觉可行,但是并没有能写出来):
构造一个匚字型,形如:
n=5,m=7   n=5,m=14
11100        11110
01000        10100
11100        10100
01000        10100
00000        11110
按照以上样子构造即可,构造完后多出来的雷往中间空出来的地方埋就行了,最后注意特判m较小的情况。(其实感觉可以方法一方法二结合写起来更简单一些)

代码实现(方法1)

#include <bits/stdc++.h>
int main() {
    std::cin.tie(nullptr)->sync_with_stdio(false);
    auto bury = [&](std::vector<std::string>& adj, int num, int k) {
        int len = (num + 1) / 2;
        for (int i = 0; i < len; ++i) {
            adj[i][len - i - 1]++;
        }
        for (int i = 0; i < len - 1; ++i) {
            adj[i][len - i - 2]++;
        }
        adj[k][k] += num % 2 == 0;
    };
    int tt;
    for (std::cin >> tt; tt--;) {
        int n, m;
        std::cin >> n >> m;
        std::vector<std::string> ans(n, std::string(n, '0'));
        if (m <= 2 * n) {
            if (n == 5 && m == 10) {
                for (int i = 0; i < n; ++i) {
                    for (int j = 0; j < i; ++j) {
                        ans[j][n - i - 1]++;
                    }
                }
            } else bury(ans, m, n - 1);
        } else {
            int now = n;
            while (m >= 2 * now + 1 && now) {
                m -= 2 * now - 1;
                now--;
                for (int i = 0; i < now; ++i) {
                    ans[i][now]++, ans[now][i]++;
                }
                ans[now][now]++;
            }
            if (m >= 2 && m < 2 * now - 1) {
                ans[0][now - 1]++, ans[now - 1][0]++;
                m -= 2;
            }
            if (m) {
                if (now == 5 && m == 10) {
                    for (int i = 0; i < now; ++i) {
                        for (int j = 0; j < i; ++j) {
                            ans[j][now - i - 1]++;
                        }
                    }
                } else if (now == 3 && m == 1) {
                    ans[now - 1][now - 1]++;
                } else if (now == 4 && m == 8) {
                    bury(ans, m, now - 1);
                    ans[0][0]++, ans[now - 1][now - 1]--;
                } else {
                    if ((now == 4 && (m == 3 || m == 4)) || (now == 3 && m == 2) || (now == 5 && m == 5)) {
                        ans[0][now - 1]--, ans[now - 1][0]--;
                        if (now == 4 && m == 4) {
                            ans[now - 1][now - 1]--;
                            ans[0][0]++;
                        }
                        m += 2;
                    }
                    if (now >= 5) {
                        if (now - (m + 1) / 2 == 2) {
                            ans[now - 2][now - 1]++, ans[now - 1][now - 2]++;
                            m -= 2;
                        }
                        if ((m & 1) && !(now == 5 && m == 9)) {
                            m += 1;
                            ans[n - 1][n - 1]--;
                        }
                    }
                    if (m) {
                        bury(ans, m, now - 1);
                    }
                }
            } else if (now == 3 || now == 2) {
                ans[0][now - 1]--, ans[now - 1][0]--;
                ans[1][1]++, ans[0 + 2 * (now == 3)][0 + 2 * (now == 3)]++;
            }
        }
        std::cout << "Yes" << '\n';
        for (int i = 0; i < n; ++i) {
            std::cout << ans[i] << "\n";
        }
    }
}

H - 随机栈

分析

队友写的,题也没有看,贴一下队友代码和思路:
这题实际上是求这个随机栈的出栈序列是非降序的概率,然后求逆元就行。那么我们只需要求出这个概率的分子,分母,最后通过费马小定理用快速幂求出逆元就行
fenziksm(fenmu,mod2)
求分子分母的过程就是利用古典概型分别求出总共的情况数(即分母),非降序的情况数(即分子)。
求分母的过程就是每次入栈 -1 时要乘上当前栈的大小,就能算出总共的情况数;
求分子的过程就是每次入栈 -1 时要乘上当前栈顶元素的个数,表示这些元素都能达到出栈序列非降序。
特别的当无法达成出栈序列是非降序时,概率为0,将分子置0就行

代码实现

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
constexpr ll mod = 998244353;

//快速幂
constexpr int P = 998244353; 
ll ksm(ll a , ll b) {
    ll res = 1;
    while (b) {
        if (b & 1) res = (ll)res * a % P;
        a = (ll)a * a % P; 
        b >>= 1;
    }
    return res;
}
int main() {
    cin.tie(nullptr)->sync_with_stdio(false);

    ll n , pren = 0 , fenzi = 1 , fenmu = 1;
    cin >> n;
    n *= 2;
    priority_queue <ll, vector<ll>, greater<ll>> ql;
    vector<ll> a(n+2);
    map<ll , ll> mp; 
    for (int i=1; i <= n; ++ i) {
        cin >> a[i];
        if (a[i] == -1) {
            ll len = (ll)ql.size();
            fenmu = fenmu % mod * len % mod;
            fenzi = fenzi % mod * mp[ql.top()] % mod;
            pren = max(ql.top() , pren);
            mp[ql.top()]--;
            ql.pop();
        } else if (a[i] != -1) {
            ql.push(a[i]);
            mp[a[i]] ++;
            if (a[i] < pren) {
                fenzi = 0;
            }
        }
    } 
    ll ans = fenzi * ksm(fenmu , mod - 2) % mod;
    std::cout << ans << "\n";
}

I - 378QAQ 和字符串 (枚举+hash+二分)

分析

枚举p,每次从p到n开始查找,每次跳过iip的lcp,以i % p为起点,统计一下以此起点时需要修改的数量,并且标记一下i % p已经被修改过了,最后判断一下以p为循环节需要修改的数量是否小于k即可。(很极限的创过去了,队友还在想着如何用卷积卷过Test4 “v(〓 ̄(∵エ∵) ̄〓)v”)

代码实现

#include <bits/stdc++.h>
using ULL = unsigned long long;
int main() {
    std::cin.tie(nullptr)->sync_with_stdio(false);
    int tt;
    for (std::cin >> tt; tt--;)[&] {
        int n, k;
        std::cin >> n >> k;
        std::string s;
        std::cin >> s;
        constexpr int P = 131;
        std::vector<ULL> h(n + 1), p(n + 1);
        p[0] = 1;
        for (int i = 0; i < n; ++i) {
            h[i + 1] = h[i] * P + s[i];
            p[i + 1] = p[i] * P; 
        }
        auto get = [&](int l, int r) { 
            return h[r] - h[l - 1] * p[r - l + 1];
        };
        auto lcp = [&](int a, int b) {
            int l = 0, r = n - b;
            while (l < r) {
                int mid = l + r >> 1;
                if (get(a + 1, a + 1 + mid) == get(b + 1, b + 1 + mid)) {
                    l = mid + 1;
                } else {
                    r = mid;
                }
            }
            return r;
        };
        std::vector<int> vis(n);
        for (int p = n / 4 + 1; p <= n / 2; ++p) {
            int cost = 0;
            for (int i = p; i < n && cost <= k; ++i) {
                i += lcp(i - p, i);
                if (i == n) break;
                int x = i % p;
                if (vis[x] != p) {
                    vis[x] = p;
                    std::vector<int> cnt(26);
                    int res = 0, tot = 0;
                    for (int j = x; j < n; j += p) {
                        tot += 1;
                        res = std::max(res, ++cnt[s[j] - 'a']);
                    }
                    cost += tot - res;
                }
            }
            if (cost <= k) {
                std::cout << "Yes" << '\n';
                return ;
            }
        }
        std::cout << "No" << '\n';
    } ();
}

J - 排列与合数 (全排列)

分析

长度只有5,全排列暴力枚举即可,最后代码队友写的

代码实现

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
//线性筛
constexpr int maxn = 1e6 + 10; 
int pnl[maxn + 10], cnt;//pnl
int st[maxn + 10];//索引为质数值就是0
void init_primes() {
    st[0]=1;
    st[1]=1;
    for (int i=2; i <= maxn; ++i) {
        if (st[i] == 0){
            pnl[cnt++] = i;
        }
        for (int j=0; pnl[j] <= maxn/i; ++j){
            st[pnl[j]*i] = 1;
            if (i%pnl[j] == 0) {
                break;
            }
        }
    }
}
int main() {
    cin.tie(nullptr)->sync_with_stdio(false);
    
    init_primes();

    int tt = 1;
    cin >> tt;
    while (tt--) [&]{
        int n , res;
        cin >> n;
        vector<int> cur(5);
        for (int j=4; j>=0; -- j) { 
            cur[j] = n % 10;
            n /= 10;
        }
        sort(cur.begin(), cur.end());
        do {
            int nn = 0;
            for (int j=0; j < 5; ++ j) {
                nn = nn * 10 + cur[j];
            }
            if (1e4 <= nn && st[nn] == 1) {
                std::cout << nn << '\n';
                return ;
            }
        } while (next_permutation(cur.begin() , cur.end()));
        std::cout << -1 << '\n';
    }();
}

K - 树上问题 (并查集)

分析

可以将能够互为父子的视为一个集合,随后对集合之间连边,u->v代表u可以做v的父亲节点,最后找到唯一的根节点集合(唯一一个没有入度的集合,有多个则无解)即可。

代码实现

#include <bits/stdc++.h>
struct DSU {
    std::vector<int> p, siz;
    DSU(int n) : p(n), siz(n, 1) { std::iota(p.begin(), p.end(), 0); }
    int leader(int x) {
        while (x != p[x]) x = p[x] = p[p[x]];
        return x;
    }
    bool same(int x, int y) { return leader(x) == leader(y); }
    bool merge(int x, int y) {
        x = leader(x), y = leader(y);
        if (x == y) return false;
        siz[x] += siz[y], p[y] = x;
        return true;
    }
    int size(int x) { return siz[leader(x)]; }
};
int main() {
    std::cin.tie(nullptr)->sync_with_stdio(false);
    int tt;
    for (std::cin >> tt; tt--;) {
        int n;
        std::cin >> n;
        std::vector<int> val(n);
        for (int i = 0; i < n; ++i) {
            std::cin >> val[i];
        }
        DSU dsu(n);
        std::vector<std::pair<int, int>> edges;
        for (int i = 0; i < n - 1; ++i) {
            int a, b;
            std::cin >> a >> b;
            a--, b--;
            int num = (val[a] * 2 >= val[b]) + (val[b] * 2 >= val[a]);
            if (num == 2) {
                dsu.merge(a, b);
            } else {
                if (val[a] * 2 <= val[b]) {
                    edges.emplace_back(a, b);
                } else {
                    edges.emplace_back(b, a);
                }
            }
        } 
        std::vector<int> d(n);
        std::vector<bool> use(n);
        for (auto [x, y] : edges) {
            x = dsu.leader(x), y = dsu.leader(y);
            use[x] = use[y] = true;
            d[y] += 1;
        }
        if (dsu.size(0) == n) {
            std::cout << n << '\n';
        } else {
            int root = -1, num = 0;
            for (int i = 0; i < n; ++i) {
                if (d[i] == 0 && use[i]) {
                    root = i;
                    num += 1;
                } 
            }
            if (num == 1) {
                std::cout << dsu.size(root) << '\n';
            } else {
                std::cout << 0 << '\n';
            }
        }
    }
}

L - Toxel 与 PCPC II(dp)

分析

依旧队友神力,并没有读题的机会

代码实现

#include <bits/stdc++.h>
using namespace std;
int main() {
  ios::sync_with_stdio(false);
  cin.tie(nullptr);
  #define int long long
  int n, m;
  cin >> n >> m;
  vector<int> a(m);
  for (int i = 0; i < m; ++i) {
    cin >> a[i];
  }
  auto power = [&](int x) {
    return x * x * x * x;
  };
  vector<int> dp(m + 1, 0);
  for (int i = 1; i <= m; ++i) {
    dp[i] = 2e18;
    for (int j = max<int>(0, i - 400); j < i; ++j) {
      dp[i] = min<int>(dp[i] , dp[j] + a[i - 1] + power(i - j));
    }
  }
  cout << dp[m] << '\n';
}

M - 有效算法 (二分)

分析

可以发现k越大,x可取的范围越大,因此直接二分k的最小值,判断所有bik可以取到的x是否有公共区间即可。

代码实现

#include <bits/stdc++.h>
int main() {
    std::cin.tie(nullptr)->sync_with_stdio(false);
    #define int long long
    int tt;
    for (std::cin >> tt; tt--;) {
        int n;
        std::cin >> n;
        std::vector<int> a(n), b(n);
        for (int i = 0; i < n; ++i) {
            std::cin >> a[i];
        }
        for (int i = 0; i < n; ++i) {
            std::cin >> b[i];
        }
        auto check = [&](int k) {
            int x = k * b[0];
            int L = a[0] - x, R = a[0] + x;
            for (int i = 1; i < n; ++i) {
                x = k * b[i];
                int l = a[i] - x, r = a[i] + x;
                if (r < L || l > R) {
                    return false;
                } else {
                    L = std::max(L, l);
                    R = std::min(R, r);
                }
            }
            return true;
        };
        int l = 0 , r = 1e9;
        while(l < r) {
            int mid = l + r >> 1;
            if (check(mid)) {
                r = mid;
            } else {
                l = mid + 1;
            }
        }
        std::cout << r << '\n';
    }
}
posted @   sleeeeeping  阅读(573)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
  1. 1 吹梦到西洲 恋恋故人难,黄诗扶,妖扬
  2. 2 敢归云间宿 三无Marblue
吹梦到西洲 - 恋恋故人难,黄诗扶,妖扬
00:00 / 00:00
An audio error has occurred, player will skip forward in 2 seconds.

作词 : 颀鞍

作曲 : 铃木航海

编曲 : 远藤直弥/冯帆

制作人 : 冯帆/铃木航海

(妖扬)

(妖扬)

无何化有 感物知春秋

秋毫濡沫欲绸缪 搦管相留

(黄诗扶)

留骨攒峰 留容映水秀

留观四时曾邂逅 佳人西洲

(妖扬)

(妖扬)

西洲何有 远树平高丘

云闲方外雨不收 稚子牵牛

(黄诗扶)

闹市无声 百态阴晴栩栩侔

藤衣半卷苔衣皱 岁月自无忧

(妖扬)

(妖扬)

驾马驱车 尚几程扶摇入画中 咫尺

(黄诗扶)

径曲桥横 精诚难通

(黄诗扶、妖扬)

(黄诗扶、妖扬)

盼你渡口 待你桥头

松香接地走

挥癯龙绣虎出怀袖

起微石落海连波动

描数曲箜篌线同轴

勒笔烟直大漠 沧浪盘虬

一纸淋漓漫点方圆透

记我 长风万里绕指未相勾

形生意成 此意 逍遥不游

(妖扬)

(妖扬)

日月何寿 江海滴更漏

爱向人间借朝暮 悲喜为酬

(黄诗扶)

种柳春莺 知它风尘不可救

绵绵更在三生后 谁隔世读关鸠

(妖扬)

(妖扬)

诗说红豆 遍南国未见人长久 见多少

(黄诗扶)

来时芳华 去时白头

(黄诗扶、妖扬)

(黄诗扶、妖扬)

忘你不舍 寻你不休

画外人易朽

似浓淡相间色相构

染冰雪先披琉璃胄

蘸朱紫将登金银楼

天命碧城灰土 刀弓褐锈

举手夜古泼断青蓝右

照我 萤灯嫁昼只影归洪流

身魂如寄 此世 逍遥不游

(黄诗扶)

(黄诗扶)

情一物 无木成林无水行舟

情一事 未算藏谋真还谬

情一人 积深不厚积年不旧

情一念 墨尽非空 百代飞白骤 划地为囚

(妖扬)

(妖扬)

蓝田需汲酒 惟琼浆能浇美玉瘦

至高者清难垢 至贵者润因愁

痴竭火 知她不能求

醉逢歌 知他不必候

只约灵犀过隙灵光暗相投

(黄诗扶、妖扬)

(黄诗扶、妖扬)

万籁停吹奏

支颐听秋水问蜉蝣

既玄冥不可量北斗

却何信相思最温柔

顾盼花发鸿蒙 怦然而梦

你与二十八宿皆回眸

系我 彩翼鲸尾红丝天地周

情之所至 此心 逍遥不游

吉他 : ShadOw

钢琴 : ShadOw

和声编写 : 冯帆

和声 : 黄诗扶

人声混音 : 徐志明

混音 : 冯帆

母带 : 冯帆

企划 : 三糙文化

出品公司 : Negia Entertainment Inc.

点击右上角即可分享
微信分享提示