zoukankan      html  css  js  c++  java
  • codevs1074 食物链(并查集+向量偏移)

    题目描述 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 Description

    第一行是两个整数N和K,以一个空格分隔。   

    以下K行每行是三个正整数D,X,Y,两数之间用一个空格隔开,其中 D 表示说法的种类。   

    若D=1,则表示X和Y是同类。   

    若D=2,则表示X吃Y。

    输出描述 Output Description

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

    样例输入 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

    数据范围及提示 Data Size & Hint

    输入文件  

     对7句话的分析 100 7

    1 101 1  假话

    2 1 2    真话

    2 2 3    真话

    2 3 3    假话

    1 1 3    假话

    2 3 1    真话

     1 5 5    真话

    NOI 2001 食物链(eat)


    关于向量偏移,这篇文章介绍的很好,还有配图说明。

    http://hi.baidu.com/tomspirit/item/d1f2a19b2aaf36d27a7f0158

    网上看到一句话…这种方法的本质就是要体会一句话:“陈冠希是谢霆锋情人的情人...谢霆锋是男人,所以陈冠希也是男人。”

    并查集可以很方便地动态维护对象的关系。



    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    #include <string>
    #include <vector>
    #include <stack>
    #include <queue>
    #include <set>
    #include <time.h>
    using namespace std;
    const int maxn = 50005;
    #define rep(i,f,t) for(int i = (f),_end = (t); i <= _end; ++i)
    #define debug(x) cout<<"debug "<<x<<endl;
    #define clr(c,x) memset(c,x,sizeof(c));
    int p[maxn];
    int tp[maxn];
    int n;
    void init(){
        rep(i,1,n)p[i] = i;
        clr(tp,0);
    }
    int Find(int i){
        if(i == p[i])return i;
        int pi = p[i];
        p[i] = Find(p[i]);
        tp[i] = (tp[pi]+tp[i])%3;
        return p[i];
    }
    void Union(int i,int j,int tmp){
        int x = Find(i);
        int y = Find(j);
        if(y == x)return ;
        p[y] = x;
        tp[y] = (tp[i] + tmp + 3 - tp[j]) %3;
    }
    bool same(int x,int y){
        if(Find(x) != Find(y)){
            Union(x,y,0);
            return true;
        }
        return tp[x] == tp[y];
    }
    bool eat(int x,int y){
        if(Find(x) != Find(y)){
            Union(x,y,1);
            return true;
        }
        return tp[x] == (tp[y]+2)%3;
    }
    
    int main() {
        int m,t,x,y;
        int ans = 0;
        scanf("%d%d",&n,&m);
        init();
        rep(k,1,m){
            scanf("%d%d%d",&t,&x,&y);
            if(x > n || y > n){
                ++ans;
                continue;
            }
            if(t == 1){
                if(!same(x,y))++ans;
            }else{
                if(!eat(x,y))++ans;
            }
        }
        printf("%d
    ",ans);
        return 0;
    }


    由于本题中的种类只有三种,因此也可以直接做。

    枚举x在这三个集合中的情况。

    用1 - n表示A集合,n+1 - 2n表示B集合, 2n+1 - 3n表示C集合。

    如果x和y+n在同一个集合表示x吃y,x和y在同一个集合表示x和y同类

    这样,x吃y表示为x与y+n,x+n与y+2n,x+2n与y在同一个集合

    x与y同类可以表示为x与y,x+n与y+n,x+2n与y+2n在同一集合。


    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    #include <string>
    #include <vector>
    #include <stack>
    #include <queue>
    #include <set>
    using namespace std;
    const int maxint = -1u>>1;
    #define rep(i,f,t) for(int i = (f),_end = (_end); i <= t; ++i)
    #define debug(x) cout<<"debug  "<<x<<endl;
    int n;
    const int maxn = 50000+5;
    
    struct BC{
        int p[maxn*3];
        void init(){
            rep(i,1,n*3)p[i] = i;
        }
        int Find(int i){
            return p[i]==i?i:p[i] = Find(p[i]);
        }
        void Union(int i,int j){
            i = Find(i);
            j = Find(j);
            if(i == j)return;
            p[j] = i;
        }
    }bc;
    bool same(int x,int y){
        if(bc.Find(x) == bc.Find(y+n) || bc.Find(x+n) == bc.Find(y))return false;
        bc.Union(x,y);
        bc.Union(x+n,y+n);
        bc.Union(x+n+n,y+n+n);
        return true;
    }
    bool eat(int x,int y){
        if(bc.Find(x) == bc.Find(y)){return false;}
        if(bc.Find(y) == bc.Find(x+n)){return false;}
        bc.Union(x,y+n);
        bc.Union(x+n,y+n+n);
        bc.Union(x+n+n,y);
        return true;
    }
    
    int main() {
        int m,tp,x,y;
        scanf("%d%d",&n,&m);
        int ans = 0;
        bc.init();
        rep(i,1,m){
            scanf("%d%d%d",&tp,&x,&y);
            if(x>n || y>n){ans++;continue;}
            if(tp == 1){
                if(!same(x,y))++ans;
            }else{
                if(!eat(x,y))++ans;
            }
        }
        printf("%d
    ",ans);
        return 0;
    }
    


    版权声明:本文为博主原创文章,未经博主允许不得转载。

  • 相关阅读:
    002-pythn基础-循环、编码
    001-python3 初识
    confluence6.x安装
    python+ffmpeg切割视频
    Elasticsearch6.x和Kibana6.x的安装
    django基础
    CDH的完全离线安装(ubuntu16)
    python之旅十【第十篇】paramiko模块
    解决 MariaDB无密码就可以登录的问题
    切割日志(mysql,nginx,php tomcat)使用logrotate
  • 原文地址:https://www.cnblogs.com/DSChan/p/4862008.html
Copyright © 2011-2022 走看看