洛谷P2024 [NOI2001]食物链

题目大意:

    森林里有三类动物满足A吃B,B吃C,C吃A的关系,现在有n个动物(编号从1到n),k条描述,判断这些描述中假话的个数。(1 ≤ N ≤ 5 ∗ 10^4,1 ≤ K ≤ 10^5)

    其中,描述有两类:"1 x y"表示xy同类;"2 x y"表示x吃y。

          假话有三类:x或y超过n;x吃x;与之前的描述矛盾。

 

思路:

    三类动物会形成这样的关系,将有这些关系的边都设为1;对于C到A的关系,需要先到B再到A,把路上的关系相加就变成了2(注意到时(-1)%3的结果),我们就把这样的关系设为2;若是同类本来不会有边,为了好表示关系,可以加一条边权为0的边。这样,计算一只动物x到另一只动物y的关系,可以从x沿已知的边一直走到y,再加起来沿路的权值,由于可能会转好几个圈,所以还要%3,这种算法的核心大概就是上述的过程。

    现在的问题就是一个一个走,太慢。由于同类之间也有边,所以整个森林的动物最终是相互连着的一个集合,那么可以借助并查集路径压缩的思想,每个点只记录自己与根的关系r[x],求x到y的关系直接(r[x]-r[y]+3)%3就好。

    判断过程大致如下:

      x,y是否在一个集合;

         若不在一个集合,这就是一组新的关系,一定不会产生矛盾,所以合并他们;

         若在一个集合,就求出二者的关系,在看与题中给的是否一致。

   路径压缩:

       注意既要更新r[x],又要更新f[x];    所以要存一下,t=f[x];f[x]=find(f[x]);r[x]=r[x]+r[t];(左边的图)  

 

 

         合并:

           合并的情况是右边的图,目前已知ra,要更新r[f[x]]为t,看图还是比较清晰的,t=r[y]+ra-r[x]。

 

下面是代码。

 

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 
 5 using namespace std;
 6 
 7 int f[50005],n,m,r[50005];
 8 
 9 int find(int x)
10 {
11    int t=f[x];
12     if(f[x]!=x)
13     {
14         f[x]=find(f[x]);
15         r[x]=(r[t]+r[x])%3;
16     }
17     return f[x];
18 }
19 
20 bool judge(int x,int y)
21 {
22     int xx=find(x);
23     int yy=find(y);
24     if(xx==yy)return false;
25     else return true;
26 }
27 
28 void uni(int x,int y,int ra)
29 {
30     int yy=find(y);
31     int xx=find(x);
32     f[xx]=yy;
33     r[xx]=(ra+r[y]-r[x]+3)%3;
34     return ;
35 }
36 
37 int main()
38 {
39     scanf("%d%d",&n,&m);
40     int ans=0;
41     for(int j=1;j<=n;j++)f[j]=j;
42     for(int i=1;i<=m;i++)
43     {
44         int d,a,b;
45         scanf("%d%d%d",&d,&a,&b);
46         if(a>n||b>n)
47         {
48             ans++;
49             continue;
50         }
51         if(d==2&&a==b)
52         {
53             ans++;
54             continue;
55         }
56         if(judge(a,b))uni(b,a,d-1);
57         else
58         {
59             int bb=find(b);//用find来更新r[a]和r[b] 
60             int aa=find(a); 
61             if((r[b]-r[a]+3)%3!=d-1)
62             {
63                 ans=ans+1;
64             }
65         }
66     }
67     printf("%d\n",ans);
68     return 0;
69 }

 

 

 

           

 

posted @ 2018-10-22 18:47  liqgnonqfu  阅读(150)  评论(0编辑  收藏  举报