UVALive-5731

UVALive-5731

题意

一颗 n - 1 条边的有向树,要求整棵树成为强连通图,一次操作即构建一条路(一笔画),
限制:

  • 新建的路上的所有边必须与原有的边逆向,即构建的路必须在原有的边和点上,
  • 操作构建的路可以存在公共边或公共点,
  • 一次操作构建的路只能有同一点或边一次

分析

要成为强连通图,那么在建边的时候肯定是正反边都要建了,
up[i] ,表示从 子节点 到 当前节点 i 要有多少条向上的边;
down[i] ,表示从 当前节点 i 到 子节点 要有多少条向下的边;

对1这个节点,up[1] = 1,因为实际有一条从 1 到 2 的边,那么新建的边就是 2 -> 1 的这条边;

结合这个例子模拟一下就好懂了,
从 0 开始向下搜索,
先搜到 4 之后回到 3 ,显然 4 -> 3 建边,那么 up[3] = 1 ,
再去搜 5 再回到 3 ,此时 5 -> 3 建边,那么 up[3] = 2,
回到 2 ,那么 up[2] = max(1, up[3]) = 2 ,
再去搜 1 ,回到 2,2 -> 1 建边,down[2] = 1,
关键来了,此时 min(up[2], down[2]) > 0 ,说明可以直接从 4 向 1 建一条路,然后 up[2]-- ,down[2]-- ,ans++;
多的 up[] down[] 继续向上传递即可,最后答案加上 max(up[0], down[0]);

对于这种情况,从 0 开始搜,搜到 2 后回到 1,up[1] = 1,在回到 0 ,down[0] = 1,在得到 down[0] 的值的时候注意了,ans 要直接加上 up[1] 的值,因为up[1] 无法向上传递了,所以当边的方向产生交替的时候,就要处理那些无法向上传递的值;

code

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e4 + 10;
typedef pair<int, int> P;
vector<P> G[MAXN];
int up[MAXN];
int down[MAXN];
int ans;
void dfs(int pre, int u)
{
    for(int i = 0; i < G[u].size(); i++)
    {
        P v = G[u][i];
        if(v.first == pre) continue;
        dfs(u, v.first);
        if(v.second)
        {
            up[u] += max(1, up[v.first]);
            ans += down[v.first];
        }
        else
        {
            down[u] += max(1, down[v.first]);
            ans += up[v.first];
        }
    }
    int tmp = min(up[u], down[u]);
    up[u] -= tmp; down[u] -= tmp;
    ans += tmp;
}
int main()
{
    int T, c = 1;
    scanf("%d", &T);
    while(T--)
    {
        memset(G, 0, sizeof G);
        memset(up, 0, sizeof up);
        memset(down, 0, sizeof down);
        ans = 0;
        int n;
        scanf("%d", &n);
        for(int i = 1; i < n; i++)
        {
            int x, y;
            scanf("%d%d", &x, &y);
            G[x].push_back(P(y, 1));
            G[y].push_back(P(x, 0));
        }
        dfs(-1, 0);
        printf("Case %d: %d
", c++, ans + max(up[0], down[0]));
    }
    return 0;
}
原文地址:https://www.cnblogs.com/ftae/p/6815064.html