CF1881F Minimum Maximum Distance
这题可以换根 dp,可以 dfs 序加线段树,但是还以一种更简便的做法,直接求两端都是标记点的最长路径就可以了。若路径长度是 \(L\),则答案是 \(\left\lceil\frac{L}{2}\right\rceil\)。具体做法就是和求直径一样,两次 dfs 即可。
- 至于为什么,其实很简单。设这条路径两个端点分别为 \(u,v\)。考虑不在这条路径上的点 \(i\),\(f_i\) 一定是先到路径上的某一点 \(x\) 的距离,再加上 \(f_x\),一定不会更优。
- 接着考虑在路径上的点 \(x\)。如果 \(x\) 距离 \(u\) 更近,那么 \(f_x=dist_{x,v}\)。反之亦然。显然只有取路径中点的时候最优,此时路径被分成两半,但 \(L\) 不一定是偶数,并且 \(f_x\) 应取最大值,所以要记得上取整。故最后答案为 \(\left\lceil\frac{L}{2}\right\rceil\)。
- 最后别忘了特判只有一个标记点的情况,答案应为 \(0\)。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<string>
#include<utility>
#include<vector>
#include<queue>
#include<bitset>
#include<map>
#define FOR(i,a,b) for(register int i=a;i<=b;i++)
#define ROF(i,a,b) for(register int i=a;i>=b;i--)
#define mp(a,b) make_pair(a,b)
#define pll pair<long long,long long>
#define pii pair<int,int>
#define fi first
#define se second
using namespace std;
inline int read();
typedef long long ll;
typedef double db;
const int N=2e5+5;
const int INF=0x3f3f3f3f;
int n,m,k;
int mark[N];
struct E{
int to,nex;
}edge[N<<1];
int head[N],cnt=0;
void add(int u,int v){
edge[++cnt]=(E){v,head[u]};
head[u]=cnt;
}
int c,L=0;
int dist[N];
void dfs(int u,int fa){
for(int i=head[u];i;i=edge[i].nex){
int v=edge[i].to;
if(v==fa) continue;
dist[v]=dist[u]+1;
dfs(v,u);
if(mark[v]&&dist[v]>dist[c]) c=v;
}
}
int main()
{
int T=read();
while(T--){
n=read(),k=read();
memset(mark,0,sizeof mark);
memset(head,0,sizeof head);
memset(dist,0,sizeof dist);
cnt=0;
FOR(i,1,k)
mark[read()]=1;
FOR(i,1,n-1){
int u=read(),v=read();
add(u,v);add(v,u);
}
if(k==1){
puts("0");
continue;
}
FOR(i,1,n){
if(mark[i]){
dist[i]=0;
L=0;
c=0;
dfs(i,0);
break;
}
}
dist[c]=0;
dfs(c,0);
L=dist[c];
printf("%d\n",(L+1)/2);
}
return 0;
}
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return f*x;
}
$\color{Red}\text{心怀荣耀,勇往直前。}$

浙公网安备 33010602011771号