Petrozavodsk Winter-2014. U of Latvia Contest B. Doors

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

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据