标签:reg 情况下 continue 节点 c++ push bool 思想 name
X 国有 \(n\) 座城市,\(n ? 1\) 条长度为 \(1\) 的道路,每条道路连接两座城市,且任意两座城市都能通过若干条道路相互到达,显然,城市和道路形成了一棵树。
X 国国王决定将 \(k\) 座城市钦定为 X 国的核心城市,这 \(k\) 座城市需满足以下两个条件:
第一行 \(2\) 个正整数 \(n,k\)。
接下来 \(n - 1\) 行,每行 \(2\) 个正整数 \(u,v\),表示第 \(u\) 座城市与第 \(v\) 座城市之间有一条长度为 \(1\) 的道路。
一行一个整数,表示答案。
1 2
2 3
2 4
1 5
5 6
1
钦定 \(1,2,5\) 这 \(3\) 座城市为核心城市,这样 \(3,4,6\) 另外 \(3\) 座非核心城市与核心城市的距离均为 \(1\),因此答案为 \(1\)。
这题的前置知识是会求直径。
我们可以显然证明,直径的中点肯定是 \(k\) 座核心城市之一。
伪证证明:
我们可以假设一个节点 \(x\) 是直径外,离直径最近的一个点。
显然,直径上的任意一个点到核心城市的距离,就是他到 \(x\) 的距离。
并且由于直径是树中最长的一条路径,所以没有其他点到 \(x\) 的距离大于直径端点到 \(x\) 的距离。
所以 \(x\) 在直径的时候,可以使与核心城市的距离最大的城市,其与核心城市的距离最小。
再证明 \(x\) 必须为直径的中点。
当 \(x\) 不为直径的中点时,必定有一个直径端点到 \(x\) 的距离稍稍的大那么一点点,只有当 \(x\) 为直径中点时,两个直径端点到 \(x\) 的最大值才会最小。
在找直径的时候,记录每个点的父亲,找到端点后,进行回溯,找到直径的中点。
然后贪心的思想,对于每个节点 \(i\),按照,以直径的中点为根,设 \(deep_i\) 为 \(i\) 节点的深度,\(maxdeep_i\) 表示 \(i\) 能到的最大深度。
按照能到达的最大深度减该节点的深度排序,取前 \(k\) 个数,就可以保证,与核心城市的距离最大的城市,其与核心城市的距离最小。
而且所选的点肯定是联通的。
代码如下:
#include<bits/stdc++.h>
#define rint register int
using namespace std;
int read(){
int s=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=0;c=getchar();}
while(c>='0'&&c<='9')s=(s<<1)+(s<<3)+(c^48),c=getchar();
return f?s:-s;
}
int n,k,dis[100010],vis[100010],point,faq;
int tot,head[200010],ver[200010],nxt[200010];
int pl[100010],maxdeep[100010],deep[100010];
int ans[100010],minn=-1;
bool cmp(int x,int y){
return x>y;
}
void add(int x,int y){
nxt[++tot]=head[x]; ver[tot]=y;
head[x]=tot;
}
void bfs(int s){ //找直径
memset(dis,0,sizeof dis);
memset(vis,0,sizeof vis);
queue<int>q; q.push(s); vis[s]=1;
while(q.size()) {
int x=q.front(); q.pop();
for(rint i=head[x];i;i=nxt[i]){
int y=ver[i];
if(!vis[y]){
pl[y]=x;
dis[y]=dis[x]+1;
q.push(y); vis[y]=1;
}
}
}
faq=0;
for(rint i=1;i<=n;++i)
if(dis[i]>faq) faq=dis[i],point=i;
}
void dfs(int x,int fa){ //处理深度
maxdeep[x]=deep[x];
for(rint i=head[x];i;i=nxt[i]){
int y=ver[i];
if(y==fa) continue;
deep[y]=deep[x]+1; dfs(y,x);
maxdeep[x]=max(maxdeep[x],maxdeep[y]);
}
}
int main(){
n=read(); k=read();
for(rint i=1,x,y;i<n;++i){
x=read(); y=read();
add(x,y); add(y,x);
}
bfs(1); bfs(point);
int mid_point=point;
for(rint i=1;i<=faq+1>>1;++i) mid_point=pl[mid_point];
dfs(mid_point,0);
for(rint i=1;i<=n;++i) ans[i]=maxdeep[i]-deep[i];
sort(ans+1,ans+1+n,cmp);
for(rint i=k+1;i<=n;++i) minn=max(ans[i]+1,minn);
printf("%d",minn);
return 0;
}
标签:reg 情况下 continue 节点 c++ push bool 思想 name
原文地址:https://www.cnblogs.com/LCGUO/p/12503639.html