题目传送门

题目大意

\(n\) 个节点和 \(m\) 条限制,每条限制形如 \(x,y,T\)\(x,y,N\) 分别表示 \(y\) 一定是 \(x\) 的祖先和 \(y\) 一定不是 \(x\) 的祖先。求一种可行树的构造方案,需要求出每个节点的父节点并输出。

分析

首先我们会发现如果一个节点要成为根,很显然有:
· 这个节点一定不为某个节点的后代
· 这个节点一定是所有节点的祖先
现在有了这两个条件,我们就可以随便选一个根出来了。如果没有这样的节点,也就是说没有根就可以直接输出无解。
接下来是非根节点。其实很简单,当成根来处理就好了:通过选根的方法同样选出这一轮的根,换句话说就是刨去之前已经选定的节点之后剩下的节点中的根,让它们接在已经选好的节点上。其中需要用并查集来维护祖先关系。

感觉比较简单吧。

Code

码风略丑,将就看吧。

#include<bits/stdc++.h>
using namespace std;
int n,m,father[1005],exfather[1005],un_find[1005],vis[1005];
struct node{
	int d1,d2;
	char op;
};
node edge[1000005];
int getfa(int u){
	if(un_find[u]==u) return u;
	return un_find[u]=getfa(un_find[u]); 
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++){
		scanf("%d%d %c",&edge[i].d1,&edge[i].d2,&edge[i].op);
		if(edge[i].op=='T') vis[edge[i].d1]=1;
		else vis[edge[i].d2]=1;
	}
	int root=0;
	for(int i=1;i<=n;i++)
		if(vis[i]==0) root=i;
	if(root==0){
		printf("NIE\n");
		return 0;
	}
	for(int i=1;i<=n;i++) exfather[i]=root,father[i]=-1;
	father[root]=0;
	for(int js=1;js<n;js++){
		for(int i=1;i<=n;i++){
			un_find[i]=i;
			if(father[i]!=-1) vis[i]=1;
			else vis[i]=0;
		}
		for(int i=1;i<=m;i++){
			if(edge[i].op=='T'&&father[edge[i].d2]==-1){
				vis[edge[i].d1]=1;
				if(father[edge[i].d1]==-1) un_find[getfa(edge[i].d1)]=getfa(edge[i].d2);
			}
		}
		for(int i=1;i<=m;i++)
			if(edge[i].op=='N'&&getfa(edge[i].d1)==getfa(edge[i].d2)) vis[edge[i].d2]=1;
		root=0;
		for(int i=1;i<=n;i++)
			if(vis[i]==0) root=i;
		if(root==0){
			printf("NIE\n");
			return 0;
		}
		father[root]=exfather[root];
		for(int i=1;i<=n;i++)
			if(getfa(i)==getfa(root)) exfather[i]=root;
	}
	for(int i=1;i<=n;i++) printf("%d\n",father[i]);
	return 0;
}

比较好懂吧,注释懒得敲了。

\(\sim\) ❀完结撒花❀ \(\sim\)