有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;
}