Petrozavodsk Winter-2014. U of Latvia Contest B. Doors
题目大意
若干个门上一共有 $n$ 个锁,Scrooge 每天会去按同样顺序把所有他想锁的门锁上。每个门上可能有不止一个锁,那么这些锁的顺序不一定是固定的。但是不同门上一定是按顺序锁的。现在给你若干个上锁序列,问最多能有多少个门。
题目分析
由题意可知,所有的序列中,所有 $x$ 即出现 $y$ 前面又出现在 $y$ 后面,那么说明 $x$ 和 $y$ 一定是在同一个门上。那么如果将所有相邻的点之间建立一条有向边,那么所有在同一个强连通分量中的点都属于同一个门。使用 $Tarjan$ 求解即可。
代码
#include <bits/stdc++.h> using namespace std; const int maxn = 1e5 + 5; int n, m, k; vector <int> e[maxn]; int dfn[maxn]; int low[maxn]; bool vis[maxn]; stack <int> s; int cnt = 0; int ans = 0; int ccnt = 0; vector <int> vec[maxn]; int dfs(int x){ int y; vis[x] = true; s.push(x); dfn[x] = low[x] = ++cnt; for(auto y : e[x]){ if(!dfn[y]){ dfs(y); low[x] = min(low[x], low[y]); }else if(vis[y]){ low[x] = min(low[x], low[y]); } } if(dfn[x] == low[x]){ ccnt++; vec[ccnt].push_back(x); vis[x] = false; while(s.top() != x){ vec[ccnt].push_back(s.top()); vis[s.top()] = false; s.pop(); } s.pop(); } return 0; } int main(){ int i, j; int x, y; scanf("%d%d", &m, &n); for(i=1;i<=m;i++){ scanf("%d", &k); scanf("%d", &y); for(j=2;j<=k;j++){ scanf("%d", &x); e[x].push_back(y); y = x; } } for(i=1;i<=n;i++){ if(!dfn[i]){ dfs(i); } } printf("%d\n", ccnt); for(i=1;i<=ccnt;i++){ printf("%d", (int)vec[i].size()); for(auto j : vec[i]){ printf(" %d", j); } printf("\n"); } return 0; }