HDU 6403 Card Game (基环树 + DP)

题目链接

有n张纸牌,每张牌的正反面分别写着一个数字,现在需要翻转某些纸牌使得所有纸牌正面的数字各不相同,问最少的操作次数以及操作的方案数。

首先建图,从初始时反面的数字向正面数字连边。问题实际上就是:将最少的边反向,使得每个点的入度都不超过1。

对于每一个弱联通分量,如果边数大于点数,那么必然会存在某些点的入度大于1。也就是说,建好的图中的弱联通分量,要么是树,要么是基环树。否则无解。

考虑在统计的时候dp,对于树,可以通过两遍dfs,分别统计出以每个点为根时最少的反转次数以及方案数。对于基环树,可以对于任一条构成环的边,去掉这条边后对剩下的数树进行统计,然后再看这条边对结果所产生的影响。

然而上面所有分析都建立在能够正确建图的基础上……

图论太差被关起来了.jpg

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <queue>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 200050;
const int INF = 0x3f3f3f3f;
const ll mod = 998244353;

int t, n, a, b, no, sz, ed, s, e, pos;
ll ans, sum, cnt, dp[maxn], val[maxn];
bool vis[maxn];
vector< pair<int, int> >maze[maxn];
vector<int>num;

void judge(int u)
{
    int len = maze[u].size();
    sz++;
    vis[u] = 1;
    for(int i = 0;i < len;i++)
    {
        pair<int, int> v = maze[u][i];
        ed++;
        if(!vis[v.first]) judge(v.first);
    }
}

void dfs(int u, int fa)
{
    val[u] = 0, vis[u] = 1;
    int len = maze[u].size();
    for(int i = 0;i < len;i++)
    {
        pair<int, int> v = maze[u][i];
        if(!vis[v.first])
        {
            dfs(v.first, u);
            val[u] += val[v.first] + ((v.second % 2)^1);
        }
        else if(v.first != fa)
        {
            s = u, e = v.first;
            pos = v.second;
        }
    }
}

void solve(int u, pair<int, int> pre)
{
    num.push_back(dp[u]);
    int len = maze[u].size();
    for(int i = 0;i < len;i++)
    {
        pair<int, int> v = maze[u][i];
        if(v == pre) continue;
        if(v.second == pos || v.second == (pos^1)) continue;
        dp[v.first] = dp[u] + (v.second%2 == 0 ? -1 : 1);
        solve(v.first, {u, v.second^1});
    }
}

int main()
{
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d", &n);
        for(int i = 1;i <= n*2;i++) maze[i].clear();
        for(int i = 1;i <= n;i++)
        {
            scanf("%d%d", &a, &b);
            maze[b].push_back({a, i*2 - 1});
            maze[a].push_back({b, i*2 - 2});
        }
        memset(vis, 0, sizeof(vis));
        bool flag = 1;
        for(int i = 1;i <= n*2;i++)
        {
            if(vis[i]) continue;
            sz = ed = 0;
            judge(i);
            if(ed/2 > sz)
            {
                flag = 0;
                break;
            }
        }
        if(!flag)
        {
            printf("-1 -1\n");
            continue;
        }
        memset(vis, 0, sizeof(vis));
        ans = 0, sum = 1;
        for(int i = 1;i <= n*2;i++)
        {
            if(vis[i]) continue;
            s = e = pos = -1, cnt = 0;
            dfs(i, 0);
            dp[i] = val[i];
            num.clear();
            solve(i, {0, 0});
            if(s == -1)
            {
                sort(num.begin(), num.end());
                int len = num.size();
                for(int j = 0;j < len;j++)
                {
                    if(num[j] != num[0]) break;
                    cnt++;
                }
                ans += num[0];
            }
            else
            {
                pos %= 2;
                if(dp[s] + pos == dp[e] + (pos^1)) cnt = 2;
                else cnt = 1;
                ans += min(dp[s] + pos, dp[e] + (pos^1));
            }
            sum = (sum*cnt) % mod;
        }
        printf("%lld %lld\n", ans, sum);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/NPU_SXY/article/details/82591740