1695D
结论题,大佬们画画图就出来了,蒻想了1h也没想出来。又画了一会图,懂了但也不会详细证明。
题意
现在有一棵
n
n
n 个节点的树(边权均为1),和一个未知的点
x
(
1
≤
x
≤
n
)
x(1\leq x\leq n)
x(1≤x≤n),你可以询问
k
k
k 个点,并在所有问题问完后分别得知这
k
k
k 个点与
x
x
x 的最短路径长度。你的目的是找到最小的
k
k
k,使得对于任意的
x
x
x,你都可以确定
x
x
x 的位置。
(题目中
x
x
x 的数量是不定的,但当成1个就可)
思路
首先询问的一定都是叶节点(询问叶节点的父亲节点,一定不优于询问叶节点,其他点同理),如果把所有叶节点都询问了,肯定是可以的,但当然不需要全选,考虑怎么删去。画了一会图,感觉只和分叉有关,链的长度不管是1还是多少都不会影响。
方法就是从每个叶节点开始遍历直到遇到度超过2的点(即分叉),如果它没有被访问过则这个叶节点可以删去。
比如这样一个图,分叉在1和2,那么对应的7和5可以删去一个,4和8可以删去一个。
代码
vector<int> e[maxn];
int vis[maxn];
void solve() {
int n;
cin >> n;
if(n == 1) {
cout << 0 << endl;
return;
}
for(int i = 1; i <= n; i++) {
e[i].clear();
vis[i] = 0;
}
bool flag = 1;
for(int i = 1; i < n; i++) {
int x, y;
cin >> x >> y;
e[x].pb(y);
e[y].pb(x);
if(e[x].size() > 2 || e[y].size() > 2) flag = 0;
}
if(flag) {
cout << 1 << endl;
return;
}
int ans = 0;
for(int i = 1; i <= n; i++) {
if(e[i].size() == 1) {
ans++;
int j = e[i][0], k = i;
while(e[j].size() <= 2) {
k ^= e[j][0] ^ e[j][1];
swap(j, k);
}
if(!vis[j]) {
vis[j] = 1;
ans--;
}
}
}
cout << ans << endl;
}