题意:
Vanya现在有一棵树,他想把这棵树缩小一些,因此,在合法的情形下他可以无限做这样的操作:任意选定一个节点x为根,如果这时候,x的其中两个子树都是链的情形并且长度相等,那么Vanya可以把其中一条链合并到另一条链上,或者说,直接删去其中一条链,问这样最后最少剩下多少条边?
solution:
不妨先看一下这个问题的反问题,初始给你一条链,可以选择某个点为根,在它的子树中,如果有某个子树是一条链的情形,可以选择把它复制一遍再给这个根当子树,问最后有没有办法生成给出的树
首先可以确定的是,一定存在至少一个节点,使得每次翻倍操作中它都不属于任意一条链,因为如果不存在,那么必有一次翻倍操作使得该点为根的子树不是一条链。这样的点可能有多个,不妨任取一个让它作最后生成树的根。这样这棵树有一些性质,比如它的任意一个子树内所有叶子的深度相同,因为翻倍操作是不影响叶子深度的。然后,它的所有叶子内可能存在的权值不超过两种,因为有三种及以上显然就构造不出来了。
假如能找到所有可能作为根的点再check一下这条链就找到了
不过有个很方便的确定方案,找出树的任意一条直径,直径中间的那个点就是可能为根的点
还是想象一开始的那条链,如果确定为根的那个点本身就在链中间,那么 ,因为翻倍操作不影响深度,所以最后生成的一定是一棵每个叶子都具有相同深度的树,根就是直径中点了
如果确定为根的点并不在链中间,那么它肯定偏向链的某一侧,这样,另一侧的深度就会偏长,只要翻倍一下,最后确定直径的时候一定就选到这个点了
因此先确定后用dfs check一下就能判断能否生成,若能,就确定了初始链
最后,初始链是可以继续缩短的,特判一下(长度每次可能可以除以2来着...)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#include<algorithm>
#include<cmath>
using namespace std;
const int maxn = 2E5 + 20;
int n,s,t,tp,L[maxn],from[maxn],dis[maxn],rd[maxn];
bool vis[maxn],bo[maxn];
vector <int> v[maxn];
queue <int> Q;
int Dfs(int x,int from)
{
int ret = 0;
for (int i = 0; i < v[x].size(); i++)
{
int to = v[x][i];
if (to == from) continue;
L[to] = L[x] + 1;
int now = Dfs(to,x);
if (now == -1) return -1;
if (!ret) ret = now;
else if (ret != now) return -1;
}
return ret ? ret : L[x];
}
bool Check(int x)
{
int A,B,cnt = 0; memset(bo,0,sizeof(bo));
for (int i = 0; i < v[x].size(); i++)
{
int to = v[x][i],ret;
L[to] = 1; ret = Dfs(to,x);
if (ret == -1) return 0;
if (bo[ret]) continue;
++cnt; bo[ret] = 1;
if (cnt == 1) A = ret;
else if (cnt == 2) B = ret;
else return 0;
}
if (cnt == 1)
{
while (!(A & 1)) A >>= 1;
cout << A << endl; exit(0);
}
else
{
A += B; while (!(A & 1)) A >>= 1;
cout << A << endl; exit(0);
}
}
void BFS(int x)
{
memset(vis,0,sizeof(vis));
dis[x] = 0; Q.push(x); vis[x] = 1;
while (!Q.empty())
{
int k = Q.front(); Q.pop();
for (int i = 0; i < v[k].size(); i++)
{
int to = v[k][i];
if (vis[to]) continue;
vis[to] = 1; dis[to] = dis[k] + 1;
from[to] = k; Q.push(to);
}
}
}
int main()
{
#ifdef DMC
freopen("DMC.txt","r",stdin);
#endif
cin >> n;
for (int i = 1; i < n; i++)
{
int x,y; scanf("%d%d",&x,&y);
v[x].push_back(y);
v[y].push_back(x);
}
BFS(1); int Max = 0;
for (int i = 2; i <= n; i++)
if (dis[i] > Max) Max = dis[i],s = i;
BFS(s); from[s] = Max = 0;
for (int i = 1; i <= n; i++)
if (dis[i] > Max) Max = dis[i],t = i;
for (int x = t; x; x = from[x]) rd[++tp] = x;
int mid = 1 + tp >> 1;
if (tp & 1) Check(rd[mid]);
else Check(rd[mid]),Check(rd[mid + 1]);
cout << -1 << endl;
return 0;
}