cf1532 D. Lost Tree(交互+构造+每次消去两个数)

这篇博客讨论了一种针对树形结构的最短路径查询算法。通过初始消除节点1,然后逐步删除不影响连接的叶子节点,每次至少删除两个节点,直到构建出所有边。在过程中,使用层次遍历和深度优先搜索策略,确保每次询问都能更新有效的边。最终,实现了在最多[n/2]次询问内获取树的所有边的目标。
摘要由CSDN通过智能技术生成

传送门

题意:有一个树,大小n(2<=n<=2000),最多[n/2]次询问,得到这棵树的所有边。每次询问给定一个整数x,然后得到所有点到该点的最短距离。

题解:第一次消去点1(最开始顺便取个点然后分层),然后每次询问“叶子节点”,同时删去“无影响”(不能再连的点)的点,每次至少可以删去两个点,即叶子节点,与叶子节点相连的点(注意,如果与叶子节点相连的点的深度与查询的点x相等,那么也不能再连了)。(自己想一想吧)

总结:这种构造问题,应该有严格的规律!!!

代码:

#include <bits/stdc++.h>
// #define int long long
// #define ll long long
#define pb push_back
#define dbg(x) cout << #x << "===" << x << endl
#define ed(x, y) (x) * n + (y)
using namespace std;
const int N = 2e3 + 10;
const int mod = 1e9 + 7;

int n, a[N], b[2][N], cnt;
int vis[N], dep[N];
vector<int> g[N];
int mp[N * N];
int cx;
signed main() {
    cin >> n;
    int i, j, k;
    int mx = 0, t, Mi, Mx;
    //初始化
    printf("? %d\n", 1);
    fflush(stdout);
    for (i = 1; i <= n; i++) {
        scanf("%d", &dep[i]);
        mx = max(mx, dep[i]);
        g[dep[i]].pb(i);
    }
    vis[1] = 1;
    for (auto j : g[1]) {
        Mi = 1, Mx = j;
        mp[ed(Mi, Mx)] = 1;
        b[0][++cnt] = Mi;
        b[1][cnt] = Mx;
    }
    queue<int> q;
    for (i = mx; i >= 0; i--) {
        for (auto j : g[i]) {
            if (!vis[j]) q.push(j);
        }
    }
    for (i = 0; i <= mx; i++) g[i].clear();
    mx = 0;
    //不断消
    while (!q.empty()) {
        if (cnt == n - 1) break;
        int x = q.front();
        q.pop();
        if (vis[x]) continue;
        printf("? %d\n", x);
        fflush(stdout);
        for (i = 1; i <= n; i++) {
            scanf("%d", &a[i]);
            if (vis[i]) continue;
            mx = max(mx, a[i]);
            g[a[i]].pb(i);
        }
        for (i = 0; i < min(2, mx); i++) {
            t = g[i][0];
            for (auto j : g[i + 1]) {
                Mi = min(t, j);
                Mx = max(t, j);
                if (mp[ed(Mi, Mx)]) continue;
                mp[ed(Mi, Mx)] = 1;
                b[0][++cnt] = Mi;
                b[1][cnt] = Mx;
                if (i == 1 && dep[j] == dep[x]) vis[j] = 1;
            }
            vis[t] = 1;
        }
        for (i = 0; i <= mx; i++) g[i].clear();
        mx = 0;
    }
    printf("!\n");
    fflush(stdout);
    for (i = 1; i <= cnt; i++) {
        printf("%d %d\n", b[0][i], b[1][i]);
        fflush(stdout);
    }
    return 0;
}

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值