P2024 [NOI2001]食物链(洛谷)

  题目传送门

  核心解法(并查集):

    关键要理清楚这些关系是怎样的:即通过了解题意可知:假设现在有如下三种动物类型:老虎,老虎的食物,老虎的天敌。所输入的x y都只满足这三种类型(输入m次去判断关系),x y是两个独立的未知种类的动物(即不知道是老虎本体、食物还是天敌)。现在需要通过构建且查询个体间的关系,因此我们需要使用并查集,只不过并查集从维护两者间的关系到维护三者间的关系。并且并查集的模板仍然适用,可以直接套,只不过初始化时元素个数由n变为3n。原因如下:以前只需要判断是否为同一组即可所以设置为n,但是此处有本体、食物、天敌这三个组别,不只是判断为是否来自一组的问题。
    为了便于存放, 一倍存本身,二倍存食物,三倍存天敌;但我们需要注意:天敌可以吃本身,食物是吃天敌的。

实现代码:

 1 #include<bits/stdc++.h>
 2 #include <cstring>
 3 using namespace std;
 4 
 5 #define MAX_N 500005
 6 int n,m,k;
 7 int rank[MAX_N];            //树的高度 
 8 int par[MAX_N]; 
 9 int ans=0;                    //记录假话数量
10 int T[MAX_N],X[MAX_N],Y[MAX_N]; 
11 void init(){
12     for(int i=1;i<=3*n;i++){
13         par[i]=i;
14         rank[i]=0;
15     }
16 }
17 
18 int find(int x){        //查询树的根 
19     if(par[x]==x){
20         return x;
21     }else return par[x]=find(par[x]);        //在查找的过程中实际上直接将父节点连接到根节点上进行优化 
22 }
23 
24 void unite(int x,int y){
25     x=find(x);
26     y=find(y);
27     
28     if(x==y)    return;
29     
30     if(rank[x]<rank[y]){
31         par[x]=y;
32     }else{
33         par[y]=x;
34         if(rank[x]==rank[y])    rank[x]++;        //两棵树一样高时,合并在x树上后X的高度加1 
35     }
36 }
37 
38 bool same(int x,int y){
39     return find(x)==find(y);
40 } 
41 
42 int main(){
43     cin>>n>>m;
44     for(int i=1;i<=m;i++){
45         cin>>T[i]>>X[i]>>Y[i];
46     }
47     init();
48     
49     for(int i=1;i<=m;i++){
50         int t=T[i],x=X[i],y=Y[i];
51         
52         if(x<1||x>n||y<1||y>n){    //先判断边界(满足条件2)
53             ans++; 
54             continue;        //跳过去看下一句话 
55         }
56         
57         if(t==1){            //表示x  y同类时 
58             if(same(x,y+n)||same(x,y+2*n)){//如果x是y的猎物或者x是y的天敌(则代表x y这两只动物不同类),显然是谎言( 
59                 ans++;
60                 continue;
61             }else{                //下面都从老虎的视角(本体)来看 
62                 unite(x,y);        //本体和本体是一个类型的,所以要合并 
63                 unite(x+n,y+n);    //食物和食物是一个类型的(即x的食物也是y的食物)
64                 unite(x+2*n,y+2*n);     //天敌和天敌是一个类型的 (即x的天敌也是y的天敌) 
65             }
66         }else if(t==2){        //表示x吃掉y(即y是x的食物关系)时 
67             if(x==y){        //若x==y表示是同一个动物,没必要写但可以稍微加快速度 
68                 ans++;
69                 continue;
70             }
71             if(same(x,y)||same(x+2*n,y)){    //表示动物x和动物y是同类型的(都假设是本体来看)或者y是x的天敌(即y吃x),表明这是谎言 
72                 ans++;
73                 continue;
74             }else{
75                 unite(x+n,y);            // x的食物与y同类型 
76                 unite(x+2*n,y+n);        //x的天敌与y的食物同类型 
77                 unite(x,y+2*n);                //x与y的天敌同类型 
78             }
79         }
80     }
81     cout<<ans;
82     return 0;
83 }
原文地址:https://www.cnblogs.com/xwh-blogs/p/12608176.html