构造题 Codeforces2133E

https://codeforces.com/problemset/problem/2133/E

题解

第一个操作没那么好理解,因为询问了某个位置之后下一步逃犯还不能走那个位置。所以先用几个具体实例来理解这个追逃游戏。

在链上可以惊喜地发现,这个特性可以让我们在链长次询问得到逃犯的位置。而且联想一下,发现我们必须要用操作 1 问出逃犯的位置,也就是询问操作至少都要有 n 次。

接下来思考操作 2,发现操作 2 肯定是放在操作 1 前最优,肯定是先切断他能走的路,再用询问逼迫他。

然后问题就变成求,先去掉 n/4 个点,剩下一堆链的方案。

直接使用题解的方法 dp:

将树以节点 1 为根。定义绿色节点为其子树中某条路径的端点,黄色节点为其子树中某条路径的中间节点,黑色节点为我们执行操作 2 的节点。然后对每个节点:
如果它有三个或更多绿色子节点,或者有任何黄色子节点,则将其染为黑色。
否则,如果它恰好有两个绿色子节点(且其他子节点都是黑色),则将其染为黄色。
否则,将其染为绿色。

代码,直接抄题解的,写的还不错:

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
#define all(x) x.begin(), x.end()
#define vecin(name, len) vector<int> name(len); for (auto &_ : name) cin >> _;
#define vecout(v) for (auto _ : v) cout << _ << " "; cout << endl;

const int MAXN = 200005;
vector<int> adj[MAXN];
int colour[MAXN];
vector<pair<int, int>> ans;
vector<int> leaves;
bool vis[MAXN];

void dfs(int node, int parent) {
    vector<int> children;
    for (auto a : adj[node])
        if (a != parent) {
            dfs(a, node);
            children.push_back(a);
        }
    int num_1_children = 0;
    bool any_2_children = false;
    for (auto child : children) {
        if (colour[child] == 1)
            num_1_children ++;
        else if (colour[child] == 2)
            any_2_children = true;
    }
    if (any_2_children || num_1_children >= 3) {
        colour[node] = 0;
        ans.push_back({2, node + 1});
        ans.push_back({1, node + 1});
    } else if (num_1_children == 2) {
        colour[node] = 2;
    } else {
        colour[node] = 1;
        if (num_1_children == 0)
            leaves.push_back(node);
    }
}

void dfs2(int node) {
    vis[node] = true;
    ans.push_back({1, node + 1});
    for (auto a : adj[node])
        if (colour[a] != 0 && !vis[a])
            dfs2(a);
}

void solve() {
    int n; cin >> n;
    for (int i = 0; i < n; i ++)
        adj[i].clear(), vis[i] = false;
    for (int i = 0; i < n - 1; i ++) {
        int a, b; cin >> a >> b;
        a --; b --;
        adj[a].push_back(b);
        adj[b].push_back(a);
    }
    ans.clear();
    leaves.clear();
    dfs(0, -1);
    for (auto a : leaves)
        if (!vis[a])
            dfs2(a);
    cout << ans.size() << endl;
    for (auto a : ans)
        cout << a.first << " " << a.second << endl;
}

int main() {
	ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    int tt = 1;

    cin >> tt;

    while (tt--) solve();
    return 0;
}
posted @ 2025-11-17 17:41  哼唧昂叽  阅读(9)  评论(0)    收藏  举报