E. Case of Computer Network

题目链接
题意:给定n个点m条边的无向图,然后q组数对(x,y),问是否存在一种方案使m条边定向后,同时满足可以从x到y。能输出Yes,否则输出No。

n , m , q < = 2 e 5 n,m,q<=2e5 n,m,q<=2e5

感觉是将无向图定向后,问是否构成强连通图题目的升级版。

因为是无向图可以求出这个图的边双连通分量,对于在双连通分量中的 ( x , y ) (x,y) (x,y)总能到达。接着不在一个双连通分量中,如果该双连通分量不连通,那么直接输出 N o No No,因为这两个点不连通就无法到达。可以知道互连的双连通分量构成一颗树,就转化为求 l c a lca lca问题。对于 x x x y y y的路径,其从 x x x l c a ( x , y ) lca(x,y) lca(x,y)这条路一定是向上走,从 l c a ( x , y ) lca(x,y) lca(x,y) y y y这条路一定向下走,所以可以用树上差分维护,维护两个差分数组 d [ ] , d 1 [ ] d[],d1[] d[],d1[],表示某个点到其父结点的这条边上的方向是否向上/下走。如果同时存在 d [ i ] d[i] d[i] d 1 [ i ] d1[i] d1[i],那么就是不行的,因为一条边只能有一个方向。

题目可能会出现重边,出现重边很好判断,就是这个边一定不是桥,即边的两个端点一定是在一个双连通分量中。
时间复杂度: O( n l o g n nlogn nlogn)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2e5+10;
vector<int>G[maxn],dcc[maxn];
vector<pair<int,int>>G1[maxn],ins;
void add1(int x,int y)
{
    G1[x].push_back({y,G1[y].size()});
    G1[y].push_back({x,G1[x].size()-1});
}
void add(int x,int y)
{
    G[x].push_back(y); G[y].push_back(x);
}

int dfn[maxn],low[maxn],k,st[maxn];
int top,id[maxn],cnt;
void tarjan(int u,int fa)
{
    low[u]=dfn[u]=++k;
    st[++top]=u;
    for(int i=0;i<G1[u].size();i++)
    {
        if(i==fa) continue;
        auto v1=G1[u][i];
        int v=v1.first;
        if(!dfn[v]) {
            tarjan(v,v1.second);                            //传to的方向边 防止有重边
            low[u]=min(low[u],low[v]);
            if(low[v]>dfn[u]) ins.push_back({u,v});
        }
        else low[u]=min(low[u],dfn[v]);
    }
    if(low[u]==dfn[u])
    {
        dcc[++cnt].push_back(u);
        id[u]=cnt;
        while(st[top]!=u)
        {
            dcc[cnt].push_back(st[top]);
            id[st[top]]=cnt;top--;
        }
        top--;
    }
}
int par[maxn];
const int lg=20;     
int fa[maxn][lg+5];       //fa[i][j]: 从i向上跳2^j步后的点
int dep[maxn];
void dfs(int u,int fa1,int id)
{
    par[u]=id;
    dep[u]=dep[fa1]+1;                
    for(int i=0;i<G[u].size();i++)
    {
        int v=G[u][i];
        if(v==fa1) continue;
        fa[v][0]=u;
        for(int j=1;j<=lg;j++)
        fa[v][j]=fa[fa[v][j-1]][j-1];
        dfs(v,u,id);
    }
}
int lca(int x,int y)
{
    if(dep[x]<dep[y]) swap(x,y);

    for(int i=lg;i>=0;i--)
    if(dep[fa[x][i]]>=dep[y]) 
    x=fa[x][i]; 

    if(x==y) return x;    //结束循环后深度肯定一样 此时相等表明y是初始x的祖先

    for(int i=lg;i>=0;i--)
    if(fa[x][i]!=fa[y][i])
    {
        x=fa[x][i];y=fa[y][i];
    }
    return fa[x][0];    //此时x与y是不等的 但是他们的父亲节点是一样的
}
int d[maxn],d1[maxn],vis[maxn];
bool fl=false;
int n,m,q;
void dfs1(int u,int fa)
{
    vis[u]=1;
    for(auto v:G[u])
    {
        if(v==fa) continue;
        dfs1(v,u);
        d[u]+=d[v];d1[u]+=d1[v];
    }
    if(d[u]&&d1[u]) {
        fl=true;
    }
}
int main() 
{
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    cin>>n>>m>>q;
    for(int i=1;i<=m;i++)
    {
        int x,y;cin>>x>>y;
        add1(x,y);
    }
    for(int i=1;i<=n;i++)
    if(!dfn[i]) tarjan(i,-1);
    for(auto i:ins)
    add(id[i.first],id[i.second]);

    for(int i=1;i<=cnt;i++)
    if(!dep[i]) dfs(i,i,i);              //求每颗树的lca
    while(q--)
    {
        int x,y;cin>>x>>y;
        if(id[x]==id[y]) continue;
        x=id[x];y=id[y];
        if(par[x]!=par[y]) {
            fl=true;continue;
        }
        d[x]++;d[lca(x,y)]--;
        d1[y]++;d1[lca(x,y)]--;
    }
    for(int i=1;i<=cnt;i++)
    if(!vis[i]) dfs1(i,i);
    if(fl) cout<<"No\n";
    else cout<<"Yes\n";;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值