晚间测试4 哪一天她能重回我身边 神奇建图+基环树

题目描述




分析

对于 \(20\%\) 的数据,我们随便写个搜索就可以了
对于 \(100\%\) 的数据,建图的方式很神奇
我们从一张卡牌背面的数字向其正面的数字连边
这样问题就转化为了翻转最少的边,使得所有点的入度不超过一
为了方便处理,我们从正面向反面建一条权值为一的边,从反面向正面建一条权值为零的边
这样在计算反转次数是只要加上边权就可以了
首先我们要把图中所有的联通块预处理出来
如果一些点想要形成联通块,那么边数一定大于等于 \(n-1\)
而根据鸽巢原理,如果边数大于 \(n\),那么至少会有一个点的入度为 \(2\)
所以只可能有两种情况
1、联通块形成一棵树
此时树中必定有且只有一个节点的入度为 \(0\)
我们就可以枚举这一个入度为 \(0\) 的点是哪一个,然后进行树形 \(DP\)
直接暴力枚举是不行的,可以换根
2、联通块形成一个环
此时该基环树一定是一个外向树(不在环上的点的连边的方向指向环外)
环之外的点的方向一定是确定的
那么我们只要枚举环上的点是逆时针还是顺时针即可

代码

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#define rg register
inline int read() {
    rg int x = 0, fh = 1;
    rg char ch = getchar();
    while (ch < '0' || ch > '9') {
        if (ch == '-')
            fh = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        x = (x << 1) + (x << 3) + (ch ^ 48);
        ch = getchar();
    }
    return x * fh;
}
const int maxn = 2e5 + 5;
const int mod = 998244353;
int h[maxn], tot = 2;
struct asd {
    int frm, to, nxt, val;
} b[maxn];
inline void ad(int aa, int bb, int cc) {
    b[tot].frm = aa;
    b[tot].to = bb;
    b[tot].val = cc;
    b[tot].nxt = h[aa];
    h[aa] = tot++;
}
int t, n, ds, bs, f[maxn], g[maxn], cnt, sum, sto, rt, nowcnt, nowsum, A, B, bjg, ansA, ansB, cnthuan;
bool vis[maxn], huan[maxn], visA[maxn], visB[maxn];
void qk() {
    memset(h, -1, sizeof(h));
    memset(b, 0, sizeof(b));
    tot = 2, sum = 1, cnt = 0, sto = 0;
    memset(vis, 0, sizeof(vis));
    memset(f, 0, sizeof(f));
    memset(g, 0, sizeof(g));
    memset(huan, 0, sizeof(huan));
    memset(visA, 0, sizeof(visA));
    memset(visB, 0, sizeof(visB));
}
void dfs(int now) {
    ds++;
    vis[now] = 1;
    for (rg int i = h[now]; i != -1; i = b[i].nxt) {
        rg int u = b[i].to;
        bs++;
        if (!vis[u])
            dfs(u);
    }
}
//找出联通块
void dfs2(int now, int fa) {
    for (rg int i = h[now]; i != -1; i = b[i].nxt) {
        rg int u = b[i].to;
        if (u == fa)
            continue;
        dfs2(u, now);
        g[now] += g[u] + b[i].val;
    }
}
void dfs3(int now, int fa) {
    if (now == rt) {
        f[now] = g[now];
        if (f[now] < nowcnt) {
            nowcnt = f[now];
            nowsum = 1;
        } else if (f[now] == nowcnt) {
            nowsum++;
        }
    }
    for (rg int i = h[now]; i != -1; i = b[i].nxt) {
        rg int u = b[i].to;
        if (u == fa)
            continue;
        f[u] = f[now];
        if (b[i].val == 1)
            f[u]--;
        else
            f[u]++;
        if (f[u] < nowcnt) {
            nowcnt = f[u];
            nowsum = 1;
        } else if (f[u] == nowcnt) {
            bjg = i;
            nowsum++;
        }
        dfs3(u, now);
    }
}
//换根
void findit(int now, int fa) {
    huan[now] = 1;
    cnthuan++;
    for (rg int i = h[now]; i != -1; i = b[i].nxt) {
        rg int u = b[i].to;
        if (u == fa)
            continue;
        if (!huan[u])
            findit(u, now);
        else {
            A = u, B = now, bjg = i;
        }
    }
}
//找环
void hahaA(int now) {
    visA[now] = 1;
    for (rg int i = h[now]; i != -1; i = b[i].nxt) {
        rg int u = b[i].to;
        if (visA[u] || i == bjg || i == (bjg ^ 1))
            continue;
        ansA += b[i].val;
        hahaA(u);
    }
}
//顺时针跑一遍
void hahaB(int now) {
    visB[now] = 1;
    for (rg int i = h[now]; i != -1; i = b[i].nxt) {
        rg int u = b[i].to;
        if (visB[u] || i == bjg || i == (bjg ^ 1))
            continue;
        ansB += b[i].val;
        hahaB(u);
    }
}
//逆时针跑一遍
int main() {
    t = read();
    rg int aa, bb;
    while (t--) {
        qk();
        n = read();
        for (rg int i = 1; i <= n; i++) {
            aa = read(), bb = read();
            ad(aa, bb, 1), ad(bb, aa, 0);
        }
        for (rg int i = 1; i <= n * 2; i++) {
            if (!vis[i]) {
                ds = 0, bs = 0;
                dfs(i);
                bs /= 2;
                if (!bs)
                    continue;
                if (bs > ds) {
                    sto = 1;
                    break;
                } else if (ds == bs + 1) {
                    rt = i, nowcnt = 0x3f3f3f3f, nowsum = 0;
                    dfs2(i, 0), dfs3(i, 0);
                    cnt += nowcnt;
                    sum = 1LL * sum * nowsum % mod;
                } else {
                    ansA = 0, ansB = 0, cnthuan = 0;
                    findit(i, 0);
                    if (cnthuan == 1) {
                        continue;
                    } else {
                        hahaA(A), hahaB(B);
                        if (b[bjg].val == 1)
                            ansA++;
                        else
                            ansB++;
                        if (ansA == ansB) {
                            cnt += ansA;
                            sum = sum * 2 % mod;
                        } else {
                            cnt += std::min(ansA, ansB);
                        }
                    }
                }
            }
        }
        if (sto) {
            printf("-1 -1\n");
        } else {
            printf("%d %d\n", cnt, sum);
        }
    }
    return 0;
}

posted @ 2020-10-09 18:03  liuchanglc  阅读(194)  评论(1编辑  收藏  举报