【UNR #6 E】神隐(交互)(二进制分组)

神隐

题目链接:UNR #6 E

题目大意

有一棵树,你每次询问可以给边(你不知道对于的两个点)选 0 或 1,然后它会把 0 的边删掉,把得到的每个连通块给你。
要你在不超过 logn+2 次得到每条边。

思路

考虑一个叫做二进制分组的东西,就是我们把读入的边自己编号。
然后每次问第 i 位是 0 和是 1 的分别问一次(就是把这些边标 1 其它标 0

那如果两个点恰好在其中的 log2n 个连通块中,那就是树上的一条边。

然后这样是 2log 级别的,考虑优化。
考虑如何才能区分两条边,那就是存在一次询问 A 在 B 不在,一次 B 在 A 不在。
然后我们考虑这么一个方法,就是我们把 02TT 是询问次数,下同)里面 1 的个数为 T/2 的给找出来。
(其实你找任意个数都可以,但是我们要最大化,避免不能对应所有的边)

然后这个大小是 (TT/2),然后我们会发现刚好大于 n1
那我们给每条边编一个上面的数的号,然后就也是二进制分组,但是每次只问 1
然后由于你限制了 1 的个数,而且这些数的编号都是不同的,所以一定会有不同的地方,再加上 1 个数一样,所以一定是各自只要有一个位置自己是 1 别人是 0

考虑怎么确定边。
发现如果一个点是叶子点,那它一定会在恰好 T/2 个询问中为单独一个点一个连通块。
然后考虑删掉这个叶子会怎样,那就把每个它所在的连通块大小减一,那如果减到了 1,我们就可以找到新的叶子节点(其实就是它的父亲)。

所以如果我们要找边,我们删一个点之间,就把那个点集枚举,找到不是它,而且没有确定父亲的点给找出来,那它们的父亲就是你删的点。

代码

#include"tree.h" #include<iostream> #include<cassert> #include<cstring> #include<cstdio> #include<algorithm> #include<vector> #include<queue> using namespace std; const int N = 131072 + 100; vector <vector <int> > q[21]; int a[N], sz[21][N], pla[21][N]; queue <int> Q; int num[N], fa[N]; bool in[N]; vector<pair<int, int> > solve(int n) { vector <int> xl; int T = (n <= 2000) ? 14 : 20; for (int i = 0; i < 1 << T; i++) { int num = 0; for (int j = 0; j < T; j++) if ((i >> j) & 1) num++; if (num == T / 2) xl.push_back(i); } for (int i = 0, now = 0; i < n - 1; i++) a[i] = xl[now], now = (now + 1) % xl.size(); for (int i = 0; i < T; i++) { vector <int> as; for (int j = 0; j < n - 1; j++) if ((a[j] >> i) & 1) as.push_back(1); else as.push_back(0); q[i] = query(as); for (int j = 0; j < q[i].size(); j++) { sz[i][j] = q[i][j].size(); for (int k = 0; k < sz[i][j]; k++) pla[i][q[i][j][k]] = j; if (sz[i][j] == 1) num[q[i][j][0]]++; } } for (int i = 0; i < n; i++) { fa[i] = -1; if (num[i] == T / 2) Q.push(i); } while (!Q.empty()) { int now = Q.front(); Q.pop(); for (int i = 0; i < T; i++) { int x = pla[i][now]; if (sz[i][x] == 1) { for (int j = 0; j < q[i][x].size(); j++) { if (q[i][x][j] == now) continue; if (in[q[i][x][j]]) continue; in[q[i][x][j]] = 1; fa[q[i][x][j]] = now; } } sz[i][x]--; if (sz[i][x] == 1) { for (int j = 0; j < q[i][x].size(); j++) { if (q[i][x][j] == now) continue; num[q[i][x][j]]++; if (num[q[i][x][j]] == T / 2) Q.push(q[i][x][j]); } } } } vector <pair<int, int> > re; for (int i = 0; i < n; i++) if (fa[i] != -1) re.push_back(make_pair(i, fa[i])); return re; }

__EOF__

本文作者あおいSakura
本文链接https://www.cnblogs.com/Sakura-TJH/p/UNR_6_E.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   あおいSakura  阅读(54)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示