CF1827E Bus Routes 题解
题目描述
\(T\) 组数据,给定一棵 \(n\) 个节点的树和 \(m\) 条树上双向路径,询问是否对于任意两个城市,都能通过不超过两条路径到达。如果不能,任意给出一组不能到达的 \((u,v)\) 。
数据范围
- \(1\le T\le 10^4,2\le n\le 5\cdot 10^5,0\le m\le 5\cdot 10^5\) 。
- \(1\le\sum n,\sum m\le 5\cdot 10^5\)。
时间限制 \(\texttt{2.5s}\) ,空间限制 \(\texttt{1GB}\) 。
分析
本题 3400 的难度评分明显虚高,但是由于放在 div1 E 的位置所以场上过的人很少。
显然叶节点的限制最强,因此只需要关心叶节点是否两两可达。
任选一个点为根,记 \(low_u\) 为叶子 \(u\) 一步能到达的最浅的点。
有解的一个必要条件是, \(\forall u,v\) , \(low_u\) 和 \(low_v\) 为祖先后代关系。
取 \(low_v\) 最深的一个叶子 \(v\) ,记 \(low_v=x\) 。
那么 \(v\) 的限制最强,原问题有解当且仅当所有叶子都能一步到达 \(x\) 。
证明:
记 \(S_u\) 为 \(u\) 一步能到达的点的集合,则 \(u\) 能两步到达 \(v\) 当且仅当 \(S_u\cap S_v\neq\varnothing\) 。
对于必要性,由于 \(x\) 是最深的 \(low\) , \((u,v)\) 合法意味着 \(x\in S_u\) 。
对于充分性,由于 \(\forall u\) , \(x\in S_u\) ,因此任意两点都可以通过 \(x\) 来中转。
以 \(x\) 为根再求一遍 \(low\) ,我们只需判断是否满足所有的 \(low\) 均为 \(x\) 。
对于构造方案:
- 如果第一遍存在 \(low_a\) 和 \(low_b\) 不为祖先后代关系,输出 \((a,b)\) 。
- 如果第二遍存在 \(low_a\neq x\) ,输出 \((a,v)\) 。
时间复杂度 \(\mathcal O((n+m)\log n)\) 。
#include<bits/stdc++.h>
#define fi first
#define se second
#define mp make_pair
#define pii pair<int,int>
using namespace std;
const int maxn=5e5+5;
int m,n,t,u,v;
int d[maxn],fa[maxn],sz[maxn],son[maxn],top[maxn];
int pos[maxn];
pii p[maxn];
vector<int> g[maxn];
vector<pii> vec;
void dfs1(int u,int f)
{
sz[u]=1;
for(auto v:g[u])
{
if(v==f) continue;
d[v]=d[u]+1,fa[v]=u,dfs1(v,u),sz[u]+=sz[v];
if(sz[v]>=sz[son[u]]) son[u]=v;
}
}
void dfs2(int u,int topf)
{
top[u]=topf;
if(son[u]) dfs2(son[u],topf);
for(auto v:g[u])
{
if(v==fa[u]||v==son[u]) continue;
dfs2(v,v);
}
}
int lca(int u,int v)
{
while(top[u]!=top[v])
{
if(d[top[u]]<d[top[v]]) swap(u,v);
u=fa[top[u]];
}
return d[u]<d[v]?u:v;
}
void work(int x)
{
for(int i=1;i<=n;i++) pos[i]=i,son[i]=0;
d[x]=1,fa[x]=0,dfs1(x,0),dfs2(x,x);
for(int i=1;i<=m;i++)
{
int u=p[i].fi,v=p[i].se,q=lca(u,v);
if(d[q]<d[pos[u]]) pos[u]=q;
if(d[q]<d[pos[v]]) pos[v]=q;
}
vec.clear();
for(int i=1;i<=n;i++) if(g[i].size()==1) vec.push_back(mp(i,pos[i]));
}
void solve()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) g[i].clear();
for(int i=1;i<=n-1;i++)
{
scanf("%d%d",&u,&v);
g[u].push_back(v),g[v].push_back(u);
}
for(int i=1;i<=m;i++) scanf("%d%d",&p[i].fi,&p[i].se);
work(1);
sort(vec.begin(),vec.end(),[&](pii x,pii y){return d[x.se]<d[y.se];});
for(int i=1;i<vec.size();i++)
if(lca(vec[i-1].se,vec[i].se)!=vec[i-1].se)
{
printf("NO\n%d %d\n",vec[i-1].fi,vec[i].fi);
return ;
}
pii cur=vec.back();
work(cur.se);
for(int i=0;i<vec.size();i++)
if(vec[i].se!=cur.se)
{
printf("NO\n%d %d\n",vec[i].fi,cur.fi);
return ;
}
printf("YES\n");
}
int main()
{
scanf("%d",&t);
while(t--) solve();
return 0;
}
本文来自博客园,作者:peiwenjun,转载请注明原文链接:https://www.cnblogs.com/peiwenjun/p/17533200.html
浙公网安备 33010602011771号