zoukankan      html  css  js  c++  java
  • NOI2001|POJ1182食物链[种类并查集 向量]

    食物链
    Time Limit: 1000MS   Memory Limit: 10000K
    Total Submissions: 65430   Accepted: 19283

    Description

    动物王国中有三类动物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),输出假话的总数。 

    Input

    第一行是两个整数N和K,以一个空格分隔。 
    以下K行每行是三个正整数 D,X,Y,两数之间用一个空格隔开,其中D表示说法的种类。 
    若D=1,则表示X和Y是同类。 
    若D=2,则表示X吃Y。

    Output

    只有一个整数,表示假话的数目。

    Sample Input

    100 7
    1 101 1 
    2 1 2
    2 2 3 
    2 3 3 
    1 1 3 
    2 3 1 
    1 5 5
    

    Sample Output

    3

    多了一种关系的种类并查集
    v[i] 0和根同类 1吃根 2被根吃
    可以发现1->0->2->1.....
    可以从向量的角度思考,比如x->y +3%3是1的话说明x吃y
    v[x]就是x->fa[x]这个向量的值

    发现D-1正好描述了X->Y这个向量的关系
    路径压缩和合并的时候都画图用向量推一下就可以了

    还有一种做法,是对每个动物x建立3个集合:x表示与x同类的动物,x+n表示要x吃的动物,x+2*n表示吃x的动物。

    一些理解
    并查集就是维护了一些关系
    种类并查集是把知道关系的东西合并,通过分配一个值来处理
    另一种做法是把同一类合并
    //
    //  main.cpp
    //  poj1182
    //
    //  Created by Candy on 31/10/2016.
    //  Copyright ? 2016 Candy. All rights reserved.
    //
    
    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    const int N=5e4+5;
    inline int read(){
        char c=getchar();int x=0,f=1;
        while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
        while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
        return x*f;
    }
    int n,m,c,x,y,ans=0;
    int fa[N],v[N];
    inline int find(int x){
        if(x==fa[x]) return x;
        int root=find(fa[x]);
        v[x]=(v[x]+v[fa[x]])%3;//x->fa[x] + fa[x]->root
        return fa[x]=root;
    }
    inline int unn(int x,int y,int op){
        int f1=find(x),f2=find(y);
        if(f1==f2){
            if((-v[y]+v[x]+3)%3!=op) return 1;
        }else{
            fa[f1]=f2;
            v[f1]=(op+v[y]-v[x]+3)%3;
        }
        return 0;
    }
    int main(int argc, const char * argv[]) {
        n=read();m=read();
        for(int i=1;i<=n;i++) fa[i]=i,v[i]=0;
        for(int i=1;i<=m;i++){
            c=read();x=read();y=read();
            if(x>n||y>n||(c==2&&x==y)){ans++;continue;}
            ans+=unn(x,y,c-1);
        }
        printf("%d",ans);
        return 0;
    }
    来自luogu题解,另一种做法
    这题显然要用并查集。因为只有3种动物,我的方法是对每个动物x建立3个集合:x表示与x同类的动物,x+n表示要x吃的动物,x+2*n表示吃x的动物。
    
    对于每个读入的描述D X Y,做以下处理:
    
    如果X或Y不再区间[1,n]中,这句是假话。
    
    D为1
    如果x+n或x+2*n与y在同一个集合中说明已知x和y不是同一种动物,这句是假话;
    
    否则,分别将x与y,x+n与y+n,x+2*n与y+2*n合并。
    
    D为2
    如果x与y在同一个集合中,说明已知x和y是同一种动物,这句是假话;
    
    如果x+2*n与y在同一个集合中,说明已知y吃x,这句是假话;
    
    否则,分别将x与y+2*n,x+n与y,x+2*n与y+n合并。
    
    <hr>
    
    说起来很复杂,实现起来其实很简单,代码见下:
    
    
    #include<iostream>
    #include<algorithm>
    using namespace std;
    int p[150001];
    int Find(int x) { return x == p[x] ? x : p[x] = Find(p[x]); }
    void Init(int n) { for (int i = 1; i <= 3 * n; i++) p[i] = i; }
    void Union(int x, int y)
    {
        int xx = Find(x), yy = Find(y);
        if (xx != yy) p[xx] = yy;
    }
    int main()
    {
        int n, k, ans = 0;
        cin >> n >> k;
        Init(n);
        for (int i = 1; i <= k; i++)
        {
            int a, x, y;
            cin >> a >> x >> y;
            if (x > n || y > n || x < 1 || y < 1) { ans++; continue; }
            if (a == 1)
            {
                if (Find(x + n) == Find(y) || Find(x + 2 * n) == Find(y)) { ans++; continue; }
                Union(x, y);
                Union(x + n, y + n);
                Union(x + 2 * n, y + 2 * n);
            }
            else
            {
                if (Find(x) == Find(y) || Find(x + 2 * n) == Find(y)) { ans++; continue; }
                Union(x, y + 2 * n);
                Union(x + n, y);
                Union(x + 2 * n, y + n);
            }
        }
        cout << ans;
    }
  • 相关阅读:
    php 有趣的头像拼图
    php基础篇-二维数组排序姐妹篇
    php基础篇-二维数组排序 array_multisort
    php应用篇-百度图片的防盗链
    《留给自己,也留给每一位在青春里迷茫找不到自己的年轻人》 爱你现在的时光——白岩松
    没有什么能一下打垮你,就像没有什么能一下拯救你
    php基础篇-双引号、单引号的区别
    TortoiseSVN Start
    cover-view文案被切割:加全角空格
    canvas不显示,必须设置canvas-id
  • 原文地址:https://www.cnblogs.com/candy99/p/6018762.html
Copyright © 2011-2022 走看看