• 【并查集&&带权并查集】BZOJ3296&&POJ1182


    bzoj1529[POI2005]ska Piggy banks

    【题目大意】

    n头奶牛m种语言,每种奶牛分别掌握一些语言。问至少再让奶牛多学多少种语言,才能使得它们能够直接或间接交流?

    【思路】

    (n+m)个点,奶牛学会某种语言就合并它和语言的节点。并查集维护联通块,答案为联通块个数-1。水,可是我跳坑了。

    我一开始做法是设总的联通块有(n+m)个,然后没合并一次减去1。其实这样是不可行的,因为我们只需要考虑奶牛(即节点1..n)有几个联通块。有可能一些语言根本没有任何奶牛掌握……

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 const int MAXN=10000+30000+50;
     4 int n,m,t;
     5 int u[MAXN],h[MAXN],appear[MAXN];
     6 
     7 int find(int x)
     8 {
     9     int tmp=x;
    10     while (u[tmp]!=tmp) tmp=u[tmp];
    11     while (u[x]!=x)
    12     {
    13         int temp=u[x];
    14         u[x]=tmp;
    15         x=temp;
    16     }
    17     return tmp;
    18 }
    19 
    20 void union_set(int fa,int fb)
    21 {
    22     if (h[fa]>=h[fb])
    23     {
    24         u[fb]=fa;
    25         if (h[fa]==h[fb]) h[fa]++;
    26     }
    27     else u[fa]=fb;
    28 }
    29 
    30 void solve()
    31 {
    32     scanf("%d%d",&n,&m);
    33     for (int i=1;i<=(n+m);i++) u[i]=i,h[i]=0;
    34     for (int i=1;i<=n;i++)
    35     {
    36         int k,lang;
    37         scanf("%d",&k);
    38         for (int j=1;j<=k;j++)
    39         {
    40             scanf("%d",&lang);
    41             int fa=find(i),fb=find(lang+n);
    42             if (fa!=fb)    union_set(fa,fb);
    43         }
    44     }
    45     int t=0;
    46     memset(appear,0,sizeof(appear));
    47     for (int i=1;i<=n;i++)
    48     {
    49         int fa=find(i);
    50         if (appear[fa]==0) t++,appear[fa]=1;
    51     }
    52     printf("%d",t-1);
    53 }
    54 
    55 int main()
    56 {
    57     solve();
    58     return 0;
    59 }

    POJ1182-[NOI01]食物链

    【题目大意】

    动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形。A吃B, B吃C,C吃A。
    现有N个动物,以1-N编号。每个动物都是A,B,C中的一种,但是我们并不知道它到底是哪一种。
    有人用两种说法对这N个动物所构成的食物链关系进行描述:
    第一种说法是"1 X Y",表示X和Y是同类。
    第二种说法是"2 X Y",表示X吃Y。
    此人对N个动物,用上述两种说法,一句接一句地说出K句话,这K句话有的是真的,有的是假的。当一句话满足下列三条之一时,这句话就是假话,否则就是真话。
    1) 当前的话与前面的某些真的话冲突,就是假话;
    2) 当前的话中X或Y比N大,就是假话;
    3) 当前的话表示X吃X,就是假话。
    你的任务是根据给定的N(1 <= N <= 50,000)和K句话(0 <= K <= 100,000),输出假话的总数。

    【思路】

    *我上一次写这道题是在一年半之前。哦。

    用0表示当前节点和u[i]同类,1表示当前节点i吃u[i],2表示u[i]吃当前节点i。

    我们设一个v,对于题目中的输入,当为1时,v=0,当为2时,v=1。

    显然如果要三个节点a、b、c,其中u[b]=c,u[a]=b,如果要压缩为u[a]=c,那么rank[a]=(rank[a]+rank[b])%3。

    然后其他推一推,详见程序吧..

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<algorithm>
     5 #include<cmath>
     6 using namespace std;
     7 const int MAXN=50000+50;
     8 int rank[MAXN],u[MAXN];
     9 int n,k,ans;
    10 
    11 int find(int x)
    12 {
    13     if (x!=u[x])
    14     {
    15         int tmp=u[x];
    16         u[x]=find(u[x]);
    17         rank[x]=(rank[x]+rank[tmp])%3;
    18     } 
    19     return u[x];
    20 } 
    21 
    22 void init()
    23 {
    24     ans=0;
    25     scanf("%d%d",&n,&k);
    26     for (int i=1;i<=n;i++) rank[i]=0,u[i]=i;
    27 }
    28 
    29 void solve()
    30 {
    31     for (int i=1;i<=k;i++)
    32     {
    33         int d,x,y;
    34         scanf("%d%d%d",&d,&x,&y);
    35         if (x>n||y>n||((d==2) && (x==y))) 
    36         {
    37             ans++;
    38             continue;
    39         }
    40         int fx=find(x),fy=find(y);
    41         int v=(d==1)?0:1;
    42         if (fx!=fy)
    43         {
    44             u[fx]=fy;
    45             rank[fx]=(rank[y]+v-rank[x]+3)%3;
    46         }
    47         else
    48             if (rank[x]!=(rank[y]+v)%3) ans++;
    49     }
    50     printf("%d",ans);
    51 }
    52 
    53 int main()
    54 {
    55     init();
    56     solve();
    57     return 0;
    58 } 
  • 相关阅读:
    04
    04 : Linux时间戳与日期相互转换
    docker rmi 删除镜像
    docker restart 命令使用
    docker rename 命令使用
    docker pull push命令使用
    docker ps 命令使用
    docker port 命令使用
    docker pause 命令使用
    docker logs 命令使用
  • 原文地址:https://www.cnblogs.com/iiyiyi/p/6063896.html
Copyright © 2020-2023  润新知