CF EDU 175

AB

C

解题思路

注意题目所描述的 penalty 是取最大值而不是求和。于是我们就可以用二分了,对于每个 mid,我们 check 的时候就贪心地来涂色:我们总是从蓝色开始涂,在红色处截止;

下面仅给出 check:

CODE
bool check(int mid) {
    int cnt = 0; // 表示涂色的次数
    bool paint = false; // 标志当前是否在涂色
    for (int i = 0; i < n; i++) {
        if (p[i] > m) {
            if (s[i] == 'R') { // 如果是红色就截止涂色
                paint = false;
            }
            else if (!paint){ // 如果是蓝色且还没开始涂色,就开始
                paint = true;
                cnt++;
            }
        }
    }
    return cnt <= k;
}

D

解题思路

记根节点在第 0 层, \(f_i\) 为从第 \(i\) 个点往下跳一共有多少种方案。则我们要的答案就是 \(\sum_{点 i 在第一层}f_i\)。考虑如何转移得到 \(f_i\)\(f_i = \sum_{点 j 在点 i 的下一层}f_j - \sum_{点 k 是点 i 的儿子}f_k + 1\)。于是从底层一层一层往上转移就好了。

CODE
std::vector<int> g[N + 5], depth(N + 5);

// 处理出层次
void dfs(int cur, int dep) {
    depth[cur] = dep;
    for (auto &to : g[cur]) {
        dfs(to, dep + 1);
    }
    return;
}

void solve()
{
    int n = 0;
    std::cin >> n;
    for (int i = 1; i <= n; i++) {
        g[i].clear();
    }

    for (int i = 2; i <= n; i++) {
        int p = 0;
        std::cin >> p;
        g[p].push_back(i);
    }

    dfs(1, 0);
    // 最底下的层
    int mx = *std::max_element(depth.begin() + 1, depth.begin() + 1 + n);
    std::vector lay(mx + 1, std::vector<int>{}); // 每一层的节点
    // ans[i] 记录的是第 i 层 f 的和
    std::vector ans(mx + 1, 0ll), f(n + 1, 0ll);
    for (int i = 2; i <= n; i++) {
        lay[depth[i]].push_back(i);
    }
    ans[mx] = lay[mx].size();
    for (auto &cur : lay[mx]) {
        f[cur] = 1;
    }

    for (int i = mx - 1; i >= 1; i--) {
        for (auto &cur : lay[i]) {
            i64 sum = 0;
            for (auto &to : g[cur]) {
                (sum += f[to]) %= Mod;
            }
            f[cur] = (1 + ans[i + 1] - sum + Mod) % Mod;
            (ans[i] += f[cur]) %= Mod;
        }
    }

    std::cout << (1 + ans[1]) % Mod << '\n';
}

E

解题思路

先手只能拿 00,那么后手肯定优先拿 01 或者 10 以限制先手。所以一个回合被两位玩家所拿掉的就是 3 个 0 和 1 个 1。若先手胜利,则最后一个回合后,剩下的只能是 001 或者两个数以上的 0 且没有 1。若用 \(C_0\) 表示 0 的数量用 \(C_1\) 表示 1 的数量,则上述两种先手胜利的情况可以分别表示为 \(C_0 - 3C_1 = -1\)\(2 \leq C_0 - 3C_1\)。对于一个区间 \(C_0 - 3C_1\) 的值,我们可以把区间中的 0 看作 1 而 1 看作 -3 来用前缀和快速地求,于是我们只用求区间和为 -1 的区间和区间大于等于 2 的区间。于是枚举右端点用 BIT 快速求符合条件的左端点的数量就好了(通过 BIT 实现单点求改和区间求和)

CODE
class BIT {
private:
    int n, d; // 因为会出现负数,所以这里有 d 的偏移
    std::vector<int> a;

    int lowbit(int x) {
        return x & -x;
    }
public:
    BIT(int _n, int _d = 0) {
        n = _n, d = _d;
        a.assign(n + d + 2, 0);
    }

    void add(int pos, int val) {
        pos += d;
        if (pos == 0) {
            return;
        }
        for (int i = pos; i <= n + d; i += lowbit(i)) {
            a[i] += val;
        }
        return;
    }

    int ask(int pos) {
        pos += d;
        int res = 0;
        for (int i = pos; i > 0; i -= lowbit(i)) {
            res += a[i];
        }
        return res;
    }
};

void solve()
{
    int n = 0;
    std::string s;
    std::cin >> n >> s;

    BIT tr(n, 3 * n);
    int sum = 0;
    tr.add(sum, 1);
    
    i64 ans = 0;
    for (int i = 1; i <= n; i++) {
        sum += (s[i - 1] == '0' ? 1 : -3);
        ans += tr.ask(sum - 2);
        ans += tr.ask(sum + 1) - tr.ask(sum);
        tr.add(sum, 1);
    }
    
    std::cout << ans << '\n';
}
posted @ 2025-03-02 17:30  Young_Cloud  阅读(27)  评论(0)    收藏  举报