题目链接:点击打开链接
题目大意:n点m边分成若干组,若u,v可相互到达,则u,v必须一组,对于组内任意两点u,v,必须保证u可以到v或者v可以到u。
解题思路:
对于可相互到达的点对,必须分在同一组。那么可以相互到达的一个集合也必须放在同一组,也就是一个强连通分量必须放在同一组。缩点之后,就可以把一个强连通分量视为一个点,原图就可以转化成DAG模型。然后就是分组的问题了。
对于组内任意两点u,v,必须保证u可以到v或者v可以到u。可以发现,一个有向路径上的两点是绝对可以满足可达的。也就是说,一条路径就是一个集合。题目要求最少的集合数,且集合没有公共点,所以题目就转化成了最小路径覆盖,直接使用匈牙利求解。
#include <set>
#include <map>
#include <cmath>
#include <stack>
#include <queue>
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
#define FIN freopen("in.txt", "r", stdin);
#define FOUT freopen("out.txt", "w", stdout);
#define lson l, mid, cur << 1
#define rson mid + 1, r, cur << 1 | 1
const int INF = 0x3f3f3f3f;
const int MAXN = 5e3 + 50;
const int MAXM = 1e5 + 50;
const int MOD = 1e9 + 7;
int n, m;
map<PII,int> mp;
struct Edge
{
int v, nxt;
} E[MAXM], ME[MAXM];
int Head[MAXN], tot, MHead[MAXN], mtot;
void edge_init()
{
tot = 0;
memset(Head, -1, sizeof(Head));
}
void edge_add(int u, int v)
{
E[tot].v = v;
E[tot].nxt = Head[u];
Head[u] = tot++;
}
void edge_init1()
{
mtot = 0;
memset(MHead, -1, sizeof(MHead));
}
void edge_add1(int u, int v)
{
ME[mtot].v = v;
ME[mtot].nxt = MHead[u];
MHead[u] = mtot++;
}
int Low[MAXN], DFN[MAXN], Stack[MAXN], Belong[MAXN];
int Index, top, scc;
bool Instack[MAXN];
int num[MAXN];
void Tarjan(int u)
{
int v;
Low[u] = DFN[u] = ++Index;
Stack[top++] = u;
Instack[u] = true;
for(int i = Head[u]; ~i; i = E[i].nxt)
{
v = E[i].v;
if( !DFN[v] )
{
Tarjan(v);
if( Low[u] > Low[v] )
Low[u] = Low[v];
}
else if(Instack[v] && Low[u] > DFN[v])
Low[u] = DFN[v];
}
if(Low[u] == DFN[u])
{
scc++;
do
{
v = Stack[--top];
Instack[v] = false;
Belong[v] = scc;
num[scc]++;
}
while( v != u);
}
}
int match[MAXN];
bool used[MAXN];
bool dfs(int u)
{
for (int i = MHead[u]; ~i; i = ME[i].nxt)
{
int v = ME[i].v;
if (u == v)
continue;
if (!used[v])
{
used[v] = true;
if (match[v] == -1 || dfs(match[v]))
{
match[v] = u;
return true;
}
}
}
return false;
}
int hungray()
{
int res = 0;
memset(match, -1, sizeof(match));
for (int i = 1; i <= scc; i++)
{
memset(used, false, sizeof(used));
if (dfs(i))
res++;
}
return res;
}
int main()
{
#ifndef ONLINE_JUDGE
FIN;
#endif // ONLINE_JUDGE
int tcase;
scanf("%d", &tcase);
while (tcase--)
{
memset(DFN, 0, sizeof(DFN));
memset(num, 0, sizeof(num));
memset(Instack, false, sizeof(Instack));
Index = scc = top = 0;
scanf("%d%d", &n, &m);
mp.clear();
edge_init();
for (int i = 0; i < m; i++)
{
int u, v;
scanf("%d%d", &u, &v);
if (mp[make_pair(u, v)] == 0)
{
edge_add(u, v);
mp[make_pair(u, v)] = 1;
}
}
for (int i = 1; i <= n; i++)
if (!DFN[i])
Tarjan(i);
mp.clear();
edge_init1();
for (int i = 1; i <= n; i++)
{
for (int j = Head[i]; ~j; j = E[j].nxt)
{
int v = E[j].v;
if (i == v)
continue;
PII p = make_pair(Belong[i], Belong[v]);
if (mp[p] == 0)
{
mp[p] = 1;
edge_add1(Belong[i], Belong[v]);
}
}
}
printf("%d\n", scc - hungray());
}
return 0;
}