peiwenjun's blog 没有知识的荒原

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;
}

posted on 2023-07-06 19:50  peiwenjun  阅读(2)  评论(0)    收藏  举报

导航