题目大意
有 \(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\)
浙公网安备 33010602011771号