对于 100% 的数据, 1 ≤ n ≤ 1000000, 1 ≤ u, v ≤ n, 保证数据合法.
显然,当n>1时答案>0
因此我们不妨枚举一个点作为根,它是必须要放的,那么深度不同的节点就被区分了。
对于一个节点,假设它有k个儿子,那么至少要在k-1个儿子子树中有信标
证明:显而易见,若有两个子树没有信标,那么靠其他的子树中的信标以及父亲以上的信标是无法区分他们的。
那么考虑如何证明这样一定合法
我们只需要考虑所有深度相同的点对(u,v),由于它们深度相同,那么它们的LCA儿子数量至少为2,因此在LCA的包含u的子树和包含v的子树中必有一个有信标,那么另一个一定走的更远(因为深度相同它们路径终点只能是LCA)
这样就可以直接自底向上贪心,加上枚举根就能 O ( n 2 ) O(n^2) O(n2)计算答案了。
如果加上一些换根优化就可以做到 O ( n ) O(n) O(n)
我们考虑另一种思路。
有结论,满足上述贪心规则下,度数>2的节点一定不会放信标
我们令根节点为一个度数>2的节点
对于深度相同的点对,可以同上证明。
现在只需要考虑深度不同的点对。
如果它们在根的同一个子树中,那么由于根节点度数>2,它们深度不同,因此一定有另一个子树中的信标区分他们。
如果在不同的子树,考虑它们如何才能不被区分,那么一定是它们先一起走到路径中点,然后再走到同一个信标,因为路径中点唯一且不是根(深度不同),那中点一定在某个子树中,这样另外一个子树中信标一定能将它们区分。
因此判掉一条链的情况以后,直接选一个度数>2的节点为根贪心即可。
时间复杂度 O ( n ) O(n) O(n)
#include
#include
#include
#include
#include
#include
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(int i=a;i>=b;--i)
#define N 1000005
using namespace std;
int m1,rd[N],n,fs[N],nt[2*N],dt[2*N],ans,f[N],g[N];
void link(int x,int y)
{
nt[++m1]=fs[x];
dt[fs[x]=m1]=y;
}
void dfs(int k,int fa)
{
for(int i=fs[k];i;i=nt[i])
{
int p=dt[i];
if(p!=fa)
{
dfs(p,k);
}
}
int cnt=0,c1=0;
for(int i=fs[k];i;i=nt[i])
{
int p=dt[i];
if(p!=fa)
{
f[k]+=f[p];
cnt++;
if(f[p]) c1++;
}
}
f[k]+=max(0,cnt-1-c1);
}
int main()
{
cin>>n;
if(n==1) {printf("0");return 0;}
fo(i,1,n-1)
{
int x,y;
scanf("%d%d",&x,&y);
rd[x]++,rd[y]++;
link(x,y),link(y,x);
}
int rt=0;
fod(i,n,1) if(rd[i]>2) {rt=i;dfs(i,0);break;}
if(rt==0) printf("1\n");
else printf("%d\n",f[rt]);
}