The solution for C.
称
引理1
对于
引理2
对于
推论
如果存在
做法
由引理 1,对于这种边的寻找,我们可以先求出每个点能到达哪些点。设
将每个点能到达的点集用并查集维护,如果出现 -1。
最终将每个点集看作一个点,枚举 1..n 将
至此,此题就完成了,复杂度为
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 2e4 + 5;
int n, m;
int in[N];
int col[N], cnt;
int top[N], tt;
int fa[N], siz[N],vis[N];
bitset<N> g[N];
vector<int> G[N], able[N];
void init()
{
for (int i = 1; i <= n; i++)
fa[i] = i, siz[i] = 1;
return;
}
int find(int x)
{
if (fa[x] == x)
return x;
return fa[x] = find(fa[x]);
}
void merge(int x, int y)
{
int fx = find(x), fy = find(y);
if (fx != fy)
fa[fx] = fy, siz[fy] += siz[fx];
return;
}
void Topo_Sort()
{ // 为了求出Topo序
queue<int> q;
for (int i = 1; i <= n; i++)
if (!in[i])
q.push(i);
while (q.size())
{
int u = q.front();
q.pop();
top[++tt] = u;
for (auto y : G[u])
if (--in[y] == 0)
q.push(y);
}
return;
}
int main()
{
cin >> n >> m;
for (int i = 1; i <= m; i++)
{
int a, b;
cin >> a >> b;
G[a].push_back(b);
in[b]++;
}
Topo_Sort();
init();
for (int i = n; i; i--)
{
int x = top[i];
for (int j = 0; j < G[x].size(); j++)
{
int y = G[x][j];
vis[j] = g[x][y];
g[x] |= g[y];
}
g[x].reset();
g[x][x] = 1;
for (int j = G[x].size() - 1; j >= 0; j--)
{
int y = G[x][j];
vis[j] |= g[x][y];
g[x] |= g[y];
if (!vis[j])
able[x].push_back(y);
}
for (auto y : able[x])
merge(able[x][0], y);
}
for (int i = 1; i <= n; i++)
{
if (find(i) == i)
col[i] = ++cnt;
if (able[i].size() && siz[find(able[i][0])] != able[i].size())
{
cout << -1;
return 0;
}
}
cout << (++cnt) << '\n';
for (int i = 1; i <= n; i++)
if (able[i].size())
printf("%d %d %d\n", col[find(i)], col[find(able[i][0])], i);
else
printf("%d %d %d\n", col[find(i)], cnt, i);
return 0;
}