2025河南萌新赛第七场比赛记录

solved 8 problems
A、C、D、I、J、K、L、M: @dbwysc

solved 8 problems
C、D、E、I、J、K、L、M: @xmjlove

M.奥数班

思路

签到题,直接 if else 就行,但是形如 “如果B比A大十倍及以上” 指的是 \(\geq\) 而非 \(>\)

代码

void solve(void) {
    i64 a, b; std::cin >> a >> b;
    if(a >= 10 * b) {
        std::cout << ">>";
    }
    else if(a > b) {
        std::cout << ">";
    }
    else if(a == b) {
        std::cout << "=";
    }
    else if(a * 10 <= b ) {
        std::cout << "<<";
    }
    else if(a < b) {
        std::cout << "<";
    }
}

J.掷骰子

思路

本题可以直接暴力。但是注意到骰子 \(A\) 的六个面上的数字均为 \(2 + 5k\) ,骰子 \(B\) 的六个面上的数字均为 \(3 + 5k\) ,因此 \(A、B\) 个投掷九次的结果和一定是 \(5\) 的倍数。同时取的最小可能 \(45\) 和 最大 \(495\) 。只要满足答案在这个区间内并且是五的倍数即可。

代码

void solve(void) {
    int n; std::cin >> n;
    int ans = 0;
    while(n--) {
        int x; std::cin >> x;
        if(x < 45 || x > 495 || x % 5) ans++;
    }
    std::cout << ans;
}

L.打游戏 or 写程序

思路

\(f_i\) 为完成 \(i\) 件事情的方案数,有 \(f_0 = 1\) 。接下来考虑状态转移:

如果 \(i \leq k\) ,说明这 \(i\) 件事之间无法插入打游戏,因此 \(f_i = f_{i - 1} + 1\)

否则想要完成第 \(i\) 件事可以由 \(f_{i - 1}\)\(f_{i - k + 1}\) 转移到。

因此状态转移方程为:

\[f_i = \left\{\begin{matrix} f_{i - 1} + 1 & i \leq k \\ f_{i - 1} + f_{i - k - 1} &i > k \end{matrix}\right. \]

代码

void solve(void) {
    int n, k; std::cin >> n >> k;
    std::vector<int> f(1e6 + 10);
    f[0] = 1;
    for(int i = 1; i <= n; i++) {
        if(i <= k) f[i] = f[i - 1] + 1;
        else f[i] = f[i - 1] + f[i - k - 1];
        f[i] %= P;
    }
    std::cout << f[n];
}

C. 灯光是我在异世界的呼喊

思路

拿 trie 维护一棵前缀树,最后求一下哪个前缀出现的次数最多即可。

代码

int son[N][26], cnt[N], idx;
int pre[N];
void insert(std::string s) {
    int p = 0;
    for(int i = 0; i < s.size(); i++) {
        int u = s[i] - 'a';
        if(!son[p][u]) son[p][u] = ++idx;
        p = son[p][u];
        pre[p]++;
    }
    cnt[p]++;
}
void solve(void) {
    int n; std::cin >> n;
    for(int i = 1; i <= n; i++) {
        std::string s;
        std::cin >> s;
        insert(s);
    }
   int ans = 0;
   for(int i = 1; i <= idx; i++) {
        if(cnt[i]) ans = std::max(ans, pre[i]);
   }
   std::cout << ans << '\n';
}

K.好奇的小夏

思路

双指针 + 前缀和。

先将原数组排序,然后维护前缀和。再用双指针维护一个滑动窗口,对于每个窗口,计算需要多少次操作能将所有数字变为右端点,如果这个需求超出了 \(k\) 次限制,左端点移动缩小窗口,否则右端点移动扩大窗口。在这个过程中记录最大值。

代码

void solve(void) {
    int n, k; std::cin >> n >> k;
    std::vector<i64> a(n + 1), s(n + 1);
    for(int i = 1; i <= n; i++) std::cin >> a[i];
    std::sort(a.begin() + 1, a.end());
    for(int i = 1; i <= n; i++) {
        s[i] = s[i - 1] + a[i];
    }
    int l = 1, cnt = 1, val = a[1];
    for(int r = 1; r <= n; r++) {
        i64 cost = a[r] * (r - l + 1) - (s[r] - s[l - 1]);
        while(cost > k) {
            l++;
            cost = a[r] * (r - l + 1) - (s[r] - s[l - 1]);
        }
        if(r - l + 1 > cnt)
            cnt = r - l + 1, val = a[r];
    }
    std::cout << cnt << " " << val;
}

A. 数组的贡献

思路

将问题转化为计算每个 \(a_i\) 左边和右边小于它的个数,记作 \(L_i\)\(R_i\) 最后的答案就变成了

\[\sum_{i = 1}^{n} L_i \times R_i \]

维护 \(L\)\(R\) 的过程可以用 RMQ,这里用了线段树。

代码

struct Info {
    i64 sum;
    int len;
    Info(): sum(0), len(0) {}
    Info(i64 x): sum(x), len(1) {}
    Info(i64 s, int l): sum(s), len(l) {}
    friend Info operator+(const Info &A, const Info &B) {
        return Info(A.sum + B.sum, A.len + B.len);
    }
};
struct SegmentTree {
    int n;
    std::vector<i64> tag;
    std::vector<Info> info;
    SegmentTree(int n_) : n(n_), tag(4 * n), info(4 * n) {};
    void pull(int p) {
        info[p] = info[2 * p] + info[2 * p + 1];
    } 
    void add(int p, i64 v) {
        tag[p] += v;
        info[p].sum += v * info[p].len;
    }
    void push(int p) {
        add(2 * p, tag[p]);
        add(2 * p + 1, tag[p]);
        tag[p] = 0;
    }
    void build(int p, int l, int r, const std::vector<i64> &a) {
        if(r - l == 1) {
            info[p] = Info(a[l]);
            return;
        }
        int m = (l + r) / 2;
        build(2 * p, l, m, a);
        build(2 * p + 1, m, r, a);
        pull(p);
    }
    void build(const std::vector<i64> &a) {
        build(1, 0, n, a);
    }
    Info query(int p, int l, int r, int x, int y) {
        if(l >= y || r <= x) {
            return {};
        }
        if(l >= x && r <= y) {
            return info[p];
        }
        int m = (l + r) / 2;
        push(p);
        return query(2 * p, l, m, x, y) + query(2 * p + 1, m, r, x, y);
    }
    Info query(int x, int y) {
        return query(1, 0, n, x, y);
    }
    void rangeAdd(int p, int l, int r, int x, int y, i64 v) {
        if(l >= y || r <= x) return;
        if(l >= x && r <= y) return add(p, v);
        int m = (l + r) / 2;
        push(p);
        rangeAdd(2 * p, l, m, x, y, v);
        rangeAdd(2 * p + 1, m, r, x, y, v);
        pull(p);
    }
    void rangeAdd(int x, int y, i64 v) {
        rangeAdd(1, 0, n, x, y, v);
    }
    void modify(int p, int l, int r, int x, const Info &v) {
        if(r - l == 1) {
            info[p] = v;
            return;
        }
        int m = (l + r) / 2;
        push(p);
        if(x < m) {
            modify(2 * p, l, m, x, v);
        } else {
            modify(2 * p + 1, m, r, x, v);
        }
        pull(p);
    }
    void modify(int x, const Info &v) {
        modify(1, 0, n, x, v);
    }
};
void solve(void) {
    int n; std::cin >> n;
    std::vector<i64> a(n + 1);
    std::vector<i64> L(n + 1), R(n + 1);
    for(int i = 1; i <= n; i++) std::cin >> a[i];
    std::vector<i64> tmp = a;
    std::sort(tmp.begin() + 1, tmp.end());
    auto ed = std::unique(tmp.begin() + 1, tmp.end());
    for(int i = 1; i <= n; i++) {
        auto it = std::lower_bound(tmp.begin() + 1, ed, a[i]);
        a[i] = (int)(it - (tmp.begin() + 1));
    }
    int M = (int)(ed - (tmp.begin() + 1));
    std::vector<i64> init(M, 0);
    SegmentTree segtree(M);
    segtree.build(init);
    for(int i = 1; i <= n; i++) {
        int x = a[i];
        i64 cnt = segtree.query(0, x + 1).sum;
        L[i] = cnt + 1;
        segtree.rangeAdd(x, x + 1,  1);
    }
    segtree.build(init);
    for(int i = n; i >= 1; i--) {
        int x = a[i];
        i64 cnt = segtree.query(0, x + 1).sum;
        R[i] = cnt + 1;
        segtree.rangeAdd(x, x + 1, 1);
    }
    i64 ans = 0;
    for(int i = 1;  i<= n; i++) ans = (ans + L[i] * R[i]) % P;
    std::cout << ans << '\n';
}

D.灯泡

思路

本题为@xmjlove的思路

注意到每个对答案有贡献的数字要么是某个数的平方,要么是某个数的平方的两倍。

代码

public static void main(String[] args) throws Exception {
    long n = Long.parseLong(br.readLine());
    pw.println((long)(Math.sqrt(n) + (long)Math.sqrt(n / 2));
    pw.flush();
}

I.Raidian 的游戏

思路

注意到 \(n \leq 8\) ,可以把 \(n\)\(m\) 的每一种排列都选出来,然后与题目中的条件一一判断,如果存在满足所有条件的排列就是答案,否则无解。

代码

void solve(void) {
    int n, m, k; std::cin >> n >> m >> k;
    std::vector<std::vector<int>> q(k, std::vector<int>(m + 1));
    for (int i = 0; i < k; i++) {
        for (int j = 0; j < m; j++) {
            std::cin >> q[i][j];
        }
        std::cin >> q[i][m]; 
    }
    std::vector<int> st(n + 1);
    std::vector<std::vector<int>> premutitaion;
    std::vector<int> b;
    auto dfs = [&](auto &&self, int step) -> void {
        if (step == m) {
            premutitaion.push_back(b);
            return;
        }
        for (int i = 1; i <= n; i++) {
            if (!st[i]) {
                st[i] = 1;
                b.push_back(i);
                self(self, step + 1);
                st[i] = 0;
                b.pop_back();
            }
        }
    };
    dfs(dfs, 0);
    int ansidx = 0;
    int cnt1 = 0;
    for (int i = 0; i < premutitaion.size(); i++) {
        int flag = 1;
        for (int j = 0; j < k; j++) {
            int cnt2 = 0;
            for (int z = 0; z < m; z++) {
                if (premutitaion[i][z] == q[j][z])
                    cnt2++;
            }
            if (cnt2 != q[j][m]) {
                flag = 0;
                break;
            }
        }
        if (flag == 1) {
            cnt1++;
            ansidx = i;
        }
    }
    if (cnt1 != 1) {
        std::cout << "-1\n";
        return;
    }
    for (int i = 0; i < m; i++) 
        std::cout << premutitaion[ansidx][i] << " ";
}

E. 优美子序列

思路

本题为@xmjlove的思路

待补充

代码

public static void main(String[] args) throws Exception {
    int n = Integer.parseInt(br.readLine());
    String s = br.readLine();
    int[][] dp = new int[27][27];
    dp[0][s.charAt(0) - 'a' + 1] = 1;
    for (int i = 1; i < n; i++) {
        int[][] temp = new int[27][27];
        for (int j = 0; j < 27; j++) {
            temp[j] = Arrays.copyOf(dp[j], 27);
        }
        int idx = s.charAt(i) - 'a' + 1;
        temp[0][idx] = 1;
        for (int j = 1; j < 27; j++) {
            if (j == idx)
                continue;
            for (int k = 0; k < 27; k++) {
                if (k == idx)
                    continue;
                if(dp[k][j] != 0)
                    temp[j][idx] = Math.max(temp[j][idx], dp[k][j] + 1);
            }
            //pw.println(i + " " + ((char)('a' + j - 1)) + "" + ((char)('a' + idx - 1)) + " " + temp[j][idx]);
        }
        for (int j = 0; j < 27; j++) {
            dp[j] = Arrays.copyOf(temp[j], 27);
        }
    }
    int ans = 0;
    for (int i = 0; i < 27; i++) {
        for (int j = 0; j < 27; j++) {
            ans = Math.max(ans, dp[i][j]);
        }
    }
    pw.println(ans);
    pw.flush();
}
posted @ 2025-08-27 17:00  dbywsc  阅读(24)  评论(0)    收藏  举报