Codeforces 555E Case of Computer Network

https://codeforces.com/problemset/problem/555/E

题意:给一张 \(n\) 个点 \(m\) 条边的无向图,可能存在多重边,给 \(p\)\((a,b)\) 对表示存在 \(a->b\) 的有向路径,询问是否存在一种构造方法(给每条边标方向)使得这 \(p\) 个条件同时成立,只用输出是否可能。

题解:

首先考虑在同一个边双连通分量中,必然可以构造出一种方案使得其中所有点两两之间可以互相到达。而剩余的割边会构造出一个森林结构。问题可简化成考虑在一棵树中,怎么给树边标方向使得各项不冲突,不同树中的两个点显然是不可达的。

那么在一颗树中,两点之间的路径一定有一部分是确定的,即 \(u->lca(u,v)->v\)。那么题目就变成了,如果维护每条边的方向。可以考虑维护每条亲子关系的树链的差分关系,具体来说,用 \(dp[i][0]\) 表示 \(i\) 号边(\(i\)节点表示的边是连向它父亲的那一条边,相当于边下放到点)向下的差分关系 \(dp[i][1]\) 表示 \(i\) 节点向上的差分关系,对于每一个条件 \((u,v)\),将 \(dp[lca(u,v)][0] + 1\)\(dp[u][0] - 1\)\(dp[lca(u,v)][1] + 1\)\(dp[v][1] - 1\),然后把亲子关系的值累加上去,就可以得到每个点的 \(dp[i][0],dp[i][1]\)\(dp[i][0]\) 如果不为 \(0\) 表示这条边需要有向下的方向,\(dp[i][1]\) 不为 \(0\) 表示它需要有向上的方向,两者不能同时存在。

代码:

/*================================================================
*
*   创 建 者: badcw
*   创建日期: 2020/6/16 15:52
*
================================================================*/
#include <bits/stdc++.h>

#define ll long long
using namespace std;

const int maxn = 2e5+50;
const int mod = 1e9+7;
ll qp(ll a, ll n, ll mod = ::mod) {
    ll res = 1;
    while (n > 0) {
        if (n & 1) res = res * a % mod;
        a = a * a % mod;
        n >>= 1;
    }
    return res;
}

int n, m, q;
vector<int> edge[maxn];
int dfn[maxn], low[maxn], idx;
int st[maxn], stsz;
int dep[maxn], dp[maxn][20];
int inWhichTree[maxn], treeNow;
int inWhichGroup[maxn], groupNow, groupRt[maxn];
int fa[maxn], fatmp[maxn];
int f[maxn][2];

void dfs(int u, int fa) {
    inWhichTree[u] = treeNow;
    fatmp[u] = fa;
    dfn[u] = low[u] = ++idx, st[++stsz] = u;
    int firstToVisFa = 1;
    for (auto v : edge[u]) {
        if (v != fa || !firstToVisFa) {
            if (!dfn[v]) {
                dfs(v, u);
                low[u] = min(low[u], low[v]);
            } else low[u] = min(low[u], dfn[v]);
        } else firstToVisFa = 0;
    }
    if (dfn[u] == low[u]) {
        ++groupNow;
        groupRt[groupNow] = u;
        do {
            inWhichGroup[st[stsz]] = groupNow;
        } while (st[stsz --] != u);
    }
}

int lca(int u, int v) {
    if (dep[u] > dep[v]) swap(u, v);
    int dt = dep[v] - dep[u];
    for (int i = 0; i < 20; ++i) if (dt >> i & 1) v = dp[v][i];
    if (u == v) return u;
    for (int i = 19; i >= 0; --i) {
        if (dp[u][i] != dp[v][i]) u = dp[u][i], v = dp[v][i];
    }
    return dp[u][0];
}

int main(int argc, char* argv[]) {
    scanf("%d%d%d", &n, &m, &q);
    for (int i = 0; i < m; ++i) {
        int u, v;
        scanf("%d%d", &u, &v);
        edge[u].push_back(v);
        edge[v].push_back(u);
    }
    for (int i = 1; i <= n; ++i) {
        if (!dfn[i]) {
            treeNow ++;
            dfs(i, 0);
        }
    }
    for (int i = 1; i <= groupNow; ++i) dp[i][0] = fa[i] = inWhichGroup[fatmp[groupRt[i]]];
    // dfs first then deal now, so if i is j's fa, i is greater than j.
    for (int i = groupNow; i >= 1; --i) dep[i] = dep[fa[i]] + 1;
    // 2e5 is less than 2^18
    for (int i = 1; i < 20; ++i) {
        for (int j = 1; j <= n; ++j) {
            dp[j][i] = dp[dp[j][i - 1]][i - 1];
        }
    }
    for (int i = 0; i < q; ++i) {
        int u, v;
        scanf("%d%d", &u, &v);
        if (inWhichTree[u] != inWhichTree[v]) {
            puts("No");
            return 0;
        }
        u = inWhichGroup[u], v = inWhichGroup[v];
        int tmp = lca(u, v);
        f[tmp][0] ++, f[tmp][1] ++;
        f[u][0] --, f[v][1] --;
    }
    // add subtree to the point
    for (int i = 1; i <= groupNow; ++i) f[fa[i]][0] += f[i][0], f[fa[i]][1] += f[i][1];
    for (int i = 1; i <= groupNow; ++i) {
        if (f[i][0] && f[i][1]) {
            puts("No");
            return 0;
        }
    }
    puts("Yes");
    return 0;
}
posted @ 2020-06-16 17:05  badcw  阅读(191)  评论(0编辑  收藏  举报