【NOI2001】食物链

原题:

 n<=5e4,k<=1e5

自己想了半天,发现了只需要立即合并真话,无视假话,那么就可以把已知的真话处理成若干个三元环,而不存在错综复杂的关系

但是这样处理起来太难写,还不一定对

最后还是决定看题解,果然发现了新知识点。。。

分组并查集,给每个动物拆ABC三个点,相同字母的分一组

盗一个洛谷题解的图,作者id是sooke,原文地址:https://www.luogu.com.cn/blog/Sooke/solution-p2024

 如果是吃的关系,就对A到B、B到C、C到A三种跨组关系都合并,如果是相等的关系,就三个组内合并

 这样若组间的点位于同一集合,就说明存在吃的关系,如果组内的点位于同一集合,就说明存在相等的关系

 两个动物的关系只有A吃B,B吃A,A等于B三种

若要判断某种关系是否存在,只需判断剩下两个关系是否存在,如果存在就表示冲突

如果三种关系都不存在就表示不知道

代码:

 1 #include<iostream>
 2 #include<cstdio>
 3 using namespace std;
 4 int n,m;
 5 int tp[151000];
 6 int ans=0;
 7 int gttp(int x){
 8     int y=x,tmp;
 9     for(;tp[y]!=y;y=tp[y]);
10     for(;x!=y;){  tmp=tp[x];  tp[x]=y;  x=tmp;}
11     return y;
12 }
13 void mg(int x,int y){  tp[gttp(x)]=tp[gttp(y)];}
14 int main(){
15     cin>>n>>m;
16     for(int i=1;i<=n*3;++i)  tp[i]=i;
17     int mk,l,r;
18     while(m --> 0){
19         scanf("%d%d%d",&mk,&l,&r);
20         if(l>n || r>n){  ++ans;  continue;}
21         if(mk==2){
22             if(gttp(l+n)==gttp(r))  ++ans;
23             else if(gttp(l)==gttp(r))  ++ans;
24             else if(gttp(l)!=gttp(r+n)){
25                 for(int i=0;i<3;++i)
26                     mg(l+i*n,r+((i+1)%3)*n);
27             }
28         }
29         else{
30             if(gttp(l+n)==gttp(r))  ++ans;
31             else if(gttp(l)==gttp(r+n))  ++ans;
32             else if(gttp(l)!=gttp(r)){
33                 for(int i=0;i<3;++i)
34                     mg(l+i*n,r+i*n);
35             }
36         }
37     }
38     cout<<ans<<endl;
39     return 0;
40 }
View Code
原文地址:https://www.cnblogs.com/cdcq/p/12302881.html