ICPC 2019-2020 North-Western Russia Regional Contest E. Equidistant(换根dp)
https://vjudge.net/problem/Gym-102411E
题意:
n个点的树上有m个特殊点,让你找到你一个点使得这个点到所有特殊点的距离相等。
思路
补:树的中心:该点到树中其他结点的最远距离 最近。
思路1
可以想到题目所求的点一定是树的中心,那么题意就变成:
- 找到树的中心
- 然后判断他是否所有到所有特殊点的距离相等即可。
也就是说:所有点距离相同等价于:
- 该点到 所有特殊节点 的最远距离 最近(树的中心)
- 该点到其他点的距离相同。
代码1
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=2e5+10, M = N * 2;
const int inf = 1e9+7;
int to[M], pre[M], h[N], idx;
int d1[N], d2[N];
int fa1[N];
int up[N];
map<int,int> mp;
void add(int a, int b)
{
to[idx] = b, pre[idx] = h[a], h[a] = idx++;
}
int a[N];
int dfs_d(int u,int f){
if(mp[u]) d1[u] =d2[u] = 0;
else d1[u] = -inf , d2[u] = -inf;
for(int i=h[u];i!=-1;i=pre[i]){
int j=to[i];
if(j==f) continue;
int d=dfs_d(j,u)+1;
if(d>d1[u]) {
d2[u]=d1[u],d1[u]=d;
fa1[u]=j;
}
else if(d>d2[u]) {
d2[u]=d;
}
}
return d1[u];
}
void dfs_u(int u,int f){
for(int i=h[u];i!=-1;i=pre[i]){
int j=to[i];
if(j==f) continue;
// cout<<j<<" " <<" " <<u<<" " <<up[u]<< " " <<d2[u]<<" "<<endl;
if(fa1[u]==j){
up[j]=max(up[u],d2[u])+1;
}
else {
up[j]=max(up[u],d1[u])+1;
}
dfs_u(j,u);
}
}
int flag=1;
int dist[N];
void dfs(int u,int v)
{
for(int i=h[u];i!=-1;i=pre[i])
{
int j=to[i];
if(j==v) continue;
dist[j]=dist[u]+1;
dfs(j,u);
// dist[u]+=dist[j];
}
}
signed main()
{
memset(h, -1, sizeof h);
int n,m;
cin>>n>>m;
for(int i=0;i<n-1;i++){
int x,y;cin>>x>>y;
add(x,y);
add(y,x);
}
for(int i=0;i<m;i++) cin>>a[i], mp[a[i]]=1;
// memset(ansd,0x3f3f,sizeof ansd);
// memset(ansup,0x3f3f,sizeof ansup);
dfs_d(1,-1);
if(mp[1]==0 )
up[1] = -inf;
dfs_u(1,-1);
int minnn = 0x3f3f3f3f3f;
int ans=0;
for (int i = 1; i <= n; i++){
// cout<<i<<" " <<ans<<" "<<d1[i]<<" " <<up[i]<<endl;
if(minnn>max(d1[i], up[i])){
minnn=max(d1[i], up[i]);
ans=i;
}
}
dfs(ans,-1);
//cout<<minnn<<endl;
for(int i=0;i<m;i++)
{
int x=a[i];
//cout<<dist[x]<<" "<<x<<endl;
if(dist[x]!=minnn)
flag=0;
}
if(flag){
cout<<"YES"<<endl;
cout << ans << endl;
}
else cout<<"NO"<<endl;
return 0;
}
思路2
到所有特殊点的距离相同等价于该点到所有特殊节点的最远的距离和最近的距离相等。
那么题意就变成:用换根dp维护
- 该点到树中其他结点的最远距离。
- 该点到树中其他结点的最近距离。
然后找到最远的距离和最近的距离相等的点即可。
代码2
#include<bits/stdc++.h>
#define mk make_pair
#define pi pair<int,int>
using namespace std;
const int maxn = 2e5+10;
int vis[maxn];
vector<int>G[maxn];
vector<pi>sf[maxn];
int mx[maxn],mn[maxn];
const int inf = 1e9+7;
int ans ;
void dfs(int u,int fa)
{
if(vis[u]) mx[u] = mn[u] = 0;
else mx[u] = -inf , mn[u] = inf;
for(auto v:G[u])
if(v!=fa)
{
dfs(v,u);
mx[u] = max(mx[u],mx[v]+1);
mn[u] = min(mn[u],mn[v]+1);
}
}
void dfs2(int u,int fa)
{
if(ans) return;
if(vis[u]) mx[u] = mn[u] = 0;
else mx[u] = -inf , mn[u] = inf;
sf[u].push_back(mk(mx[u],mn[u]));
for(auto v:G[u])
{
mx[u] = max(mx[u],mx[v]+1);
mn[u] = min(mn[u],mn[v]+1);
sf[u].push_back(mk(mx[u],mn[u]));
}
if(mx[u]==mn[u]){
ans = u;return ;
}
int maxn = -inf , minn = inf;
for(int i=G[u].size()-1;i>=0;i--)
{
int v = G[u][i];
mx[u] = max(sf[u][i].first,maxn);
mn[u] = min(sf[u][i].second,minn);
maxn = max(maxn,mx[v]+1);
minn = min(minn,mn[v]+1);
if(v!=fa) dfs2(v,u);
}
}
int main()
{
int n,m;scanf("%d%d",&n,&m);
int u,v;
for(int i=1;i<n;i++)
{
scanf("%d%d",&u,&v);
G[u].push_back(v);
G[v].push_back(u);
}
int p;
for(int i=1;i<=m;i++)
{
scanf("%d",&p);
vis[p]++;
}
dfs(1,0);
dfs2(1,0);
if(ans) printf("YES\n%d\n",ans);
else puts("NO");
}