[CF555E]Case of Computer Network

题目大意

给你一个n个点m条边的图,以及q对点(s,t),让你分配m条边的方向。问是否存在一种分配方案,使每对点的s能走到t。
1 ≤ n, m, q ≤  2105

分析

首先对于原图的边双联通分量,肯定可以找到一个分配方案,使得每个点能两两联通。
缩点之后,原图变成一棵树。现在就是分配每条树边的方向了。

树链剖分做法

枚举每个请求,然后给路径打上方向标记,如果有冲突直接输出no
时间复杂度 O(nlog2n)

一个log的算法

考虑如果是序列上该怎么做。
把每个向左(或右)的请求挂在起点、终点,然后1到n扫一遍。
现在我们树剖,就把树上的路径变成序列上的log条路径。

更优秀的做法

既然都想到了一个log的算法,为什么不直接上树呢?
把请求挂在s,t,lca上,然后从叶子节点出发,记录向上(或下)标记个数。如果一条边存在两种标记就直接输出no了。求lca可以用tarjan。

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <vector>

#define pb push_back
#define fi first
#define se second

using namespace std;

const int maxn=200005,maxm=400005;

typedef long long LL;

typedef pair<int,int> PII;

int n,m,q,top,st[maxn],f[maxn],id[maxn],tot,dfn[maxn],low[maxn],s,visit[maxn],p[maxn],ss;

int Up[maxn],Down[maxn],Up_cnt[maxn],Down_cnt[maxn],Clear[maxn],lca[maxn];

vector <int> e[maxn],te[maxn];

vector <PII> query[maxn];

PII tmp;

char c;

int read()
{
    for (c=getchar();c<'0' || c>'9';c=getchar());
    int x=c-48;
    for (c=getchar();c>='0' && c<='9';c=getchar()) x=x*10+c-48;
    return x;
}

void Tarjan(int x,int y)
{
    low[x]=dfn[x]=++tot; st[++top]=x; visit[x]=1; p[x]=ss;
    bool bz=0;
    vector <int> ::iterator it;
    for (it=e[x].begin();it!=e[x].end();it++)
    {
        if (!visit[*it])
        {
            Tarjan(*it,x); low[x]=min(low[x],low[*it]);
        }else if (*it==y)
        {
            if (bz) low[x]=min(low[x],low[y]);else bz=1;
        }else low[x]=min(low[x],low[*it]);
    }
    if (low[x]==dfn[x])
    {
        s++;
        for (;st[top+1]!=x;top--) id[st[top]]=s;
    }
}

int get(int x)
{
    if (!f[x]) return x;
    return f[x]=get(f[x]);
}

void get_lca(int x,int fa)
{
    vector <PII> ::iterator i;
    for (i=query[x].begin();i!=query[x].end();i++)
    {
        tmp=*i; lca[tmp.se]=get(tmp.fi);
    }
    vector <int> ::iterator j;
    for (j=te[x].begin();j!=te[x].end();j++) if (*j!=fa) get_lca(*j,x);
    f[x]=fa;
}

void dfs(int x,int fa)
{
    Up_cnt[x]=Up[x]; Down_cnt[x]=Down[x];
    vector <int> ::iterator i;
    for (i=te[x].begin();i!=te[x].end();i++) if (*i!=fa)
    {
        dfs(*i,x);
        Up_cnt[x]+=Up_cnt[*i]; Down_cnt[x]+=Down_cnt[*i];
    }
    Up_cnt[x]-=Clear[x]; Down_cnt[x]-=Clear[x];
    if (Up_cnt[x]>0 && Down_cnt[x]>0)
    {
        printf("No\n"); exit(0);
    }
}

int main()
{
    n=read(); m=read(); q=read();
    while (m--)
    {
        int x=read(),y=read();
        e[x].pb(y); e[y].pb(x);
    }
    for (int i=1;i<=n;i++) if (!visit[i])
    {
        ss++; Tarjan(i,0);
    }
    memset(visit,0,sizeof(visit));
    for (int i=1;i<=n;i++)
    {
        vector <int> ::iterator it;
        for (it=e[i].begin();it!=e[i].end();it++)
            if (id[i]!=id[*it] && visit[id[*it]]<i)
            {
                te[id[i]].pb(id[*it]); visit[id[*it]]=i;
            }
    }
    for (int i=0;i<q;i++)
    {
        int x=read(),y=read();
        if (p[x]!=p[y])
        {
            printf("No\n"); return 0;
        }
        x=id[x]; y=id[y];
        tmp.fi=y; tmp.se=i;
        query[x].pb(tmp);
        tmp.fi=x;
        query[y].pb(tmp); Up[x]++; Down[y]++;
    }
    get_lca(1,0);
    for (int i=0;i<q;i++) Clear[lca[i]]++;
    dfs(1,0);
    printf("Yes\n");
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值