续并查集学习笔记——Gang团伙题解

一言不合先贴题目

Description

在某城市里住着n个人,任何两个认识的人不是朋友就是敌人,而且满足: 1、 我朋友的朋友是我的朋友; 2、 我敌人的敌人是我的朋友; 所有是朋友的人组成一个团伙。告诉你关于这n个人的m条信息,即某两个人是朋友,或者某两个人是敌人,请你编写一个程序,计算出这个城市最多可能有多少个团伙?

Input

第1行为n和m,N小于1000,M小于5000; 以下m行,每行为p x y,p的值为0或1,p为0时,表示x和y是朋友,p为1时,表示x和y是敌人。

Output

一个整数,表示这n个人最多可能有几个团伙。

Sample Input

6
4
E 1 4
F 3 5
F 4 6
E 1 2

Sample Output

3

HINT

{1},{2,4,6},{3,5}

一开始的想法和ABC野兽那道题一样,可以维护每个点和祖先的关系。而且似乎更简单。于是有了下面的程序

#include<iostream>
#include<cstdio>
using namespace std;
int fa[1010],deep[1010],ans[1010][2],n,m;
int getf(int k){
    if(fa[k]!=k){
        int t=fa[k];
        fa[k]=getf(fa[k]);
        deep[k]=deep[k]+deep[t];
        deep[k]=deep[k]%2;
    }
    return fa[k];
}
int main(){
    cin>>n>>m;
    for(int i=1;i<=n;++i){
        fa[i]=i;
        deep[i]=0;
    }
    for(int i=1;i<=m;++i){
        char c[2];
        int x,y;
        scanf("%s%d%d",&c,&x,&y);
        if((c[0]==‘1‘)&&(getf(x)!=getf(y))){
            int fx=getf(x);
            int fy=getf(y);
            deep[fx]=(deep[y]-deep[x]+3)%2;
            fa[fx]=fy;
        }
        if((c[0]==‘0‘)&&(getf(x)!=getf(y))){
            int fx=getf(x);
            int fy=getf(y);
            deep[fx]=(deep[y]-deep[x]+2)%2;
            fa[fx]=fy;
        }
    }

    for(int i=1;i<=n;++i){
        fa[i]=getf(i);
        //cout<<fa[i]<<" "<<deep[i]<<endl;
        ans[fa[i]][deep[i]]++;
    }
    int add=0;
    for(int i=1;i<=n;++i)
    for(int j=0;j<=1;++j)if(ans[i][j]>0){
        add++;
    }

    cout<<add;
    return 0;
}

然后、、、、、、就爆0了。

哪里出了问题?忽然发现题设中并没有:我的敌人的朋友是我的敌人。MDZZ!这不符合逻辑啊,虽然NOI从来不讲逻辑。

重新出发,想到分点的方法。通过朋友相关联的,fa[x]=y;通过敌人相关联的,fa[x]=y+n,fa[x+n]=y。该题完美解决。楼下程序:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int n,m,ans,a[1010];
int fa[2020];
int Find(int x)  {
    if(!fa[x]||fa[x]==x)
    return fa[x]=x;
    return fa[x]=Find(fa[x]);
    }
void Union(int x,int y)  {
    x=Find(x);y=Find(y);
    if(x==y) return ;
    fa[x]=y;
    }
int main(){
    int i,x,y;
    char p[10];
    cin>>n>>m;
    for(i=1;i<=m;i++)
    {
        scanf("%s%d%d",p,&x,&y);
        if(p[0]==‘F‘)
            Union(x,y);
        else
            Union(x,y+n),Union(x+n,y);
    }
    for(i=1;i<=n;i++)
        a[i]=Find(i);
    sort(a+1,a+n+1);
    for(i=1;i<=n;i++)
        if(i==1||a[i]!=a[i-1])
            ++ans;
    cout<<ans<<endl;
    return 0;
}  

To be continue......

时间: 2024-10-01 05:07:01

续并查集学习笔记——Gang团伙题解的相关文章

【并查集】BZOJ1370- [Baltic2003]Gang团伙

[题目大意] 在某城市里住着n个人,任何两个认识的人不是朋友就是敌人,而且满足: 1. 我朋友的朋友是我的朋友: 2. 我敌人的敌人是我的朋友: 所有是朋友的人组成一个团伙.告诉你关于这n个人的m条信息,即某两个人是朋友,或者某两个人是敌人,请你编写一个程序,计算出这个城市最多可能有多少个团伙? [思路] 水………NOIP的小孩都不屑于玩…… 把i拆成i和i+n,其中i表示i的朋友,i+n表示i的敌人.对于(i,j): (1)i,j是朋友,那么合并i和j. (2)i,j不是朋友,那么合并i和j+

并查集--学习详解

[转] 文章作者:yx_th000 文章来源:Cherish_yimi (http://www.cnblogs.com/cherish_yimi/) 昨天和今天学习了并查集和trie树,并练习了三道入门题目,理解更为深刻,觉得有必要总结一下,这其中的内容定义之类的是取自网络,操作的说明解释及程序的注释部分为个人理解.并查集学习: l         并查集:(union-find sets) 一种简单的用途广泛的集合. 并查集是若干个不相交集合,能够实现较快的合并和判断元素所在集合的操作,应用很

并查集学习

从HOJ1232来学习并查集 #include<iostream> #include<vector> using namespace std; //封装好的并查集 class UF{//union find private: vector<int> v;//存储各个结点 v[i]=j 表示 第i个结点的代表元是j(所在树的根结点) public: //初始化 UF(int n){//初始化 总共有n个结点 刚开始的时候v[i]=i for(int i=0;i<=n

并查集学习2

(转) 并查集由一个整数型的数组和两个函数构成.数组pre[]记录了每个点的前导点是什么,函数find是查找,join是合并. int pre[1000 ]; int find(int x)                                                                                                         //查找根节点 {      int r=x;     while ( pre[r ] != r )

并查集学习总结

并查集作用:判断两个元素是否在一个集合内. 方式:通过记录每个元素对应的par[x]值来判断,如果两者相等,则为同组,否则为不同组. _rank[maxn]的作用:_rank[i]记录的是每一个节点所在的树的高度,查询的时候意义不大,但是当两棵树合并的时候,当树的高度小的树连接到树的高度高的树会让树的整体高度较小,而保证查询优化. 状态压缩:并查集的状态压缩通过如下代码实现: int find(int x){ if(par[x]==x) return x; }else{ return par[x

尚学堂300集学习笔记——eclipse 开发项目

编程的本质: 把现实生活中的业务逻辑用代码实现. eclipse 是一个开放源代码.基于Java的可扩展开发平台. (最初主要用来Java语言开发,但目前亦有人通过插件使其为其他计算机语言比如C++和Python的开发工具). IDE(Integrated Development Environment,集成开发环境) bin文件夹(binary 二进制) 手动编译:Java文件→Javac命令编译→编译后的二进制类文件(class文件) 在eclipse工具下,我们点击run as ,它就帮助

Javascript学习笔记:闭包题解(4)

代码: 1 var val1=0; 2 var val2=0; 3 var val3=0; 4 5 for(var i1=1;i1<=3;i1++){ 6 var i2=i1; 7 (function(){ 8 var i3=i2; 9 setTimeout(function(){ 10 val1+=i1; 11 val2+=i2; 12 val3+=i3; 13 },1); 14 })(); 15 } 16 17 setTimeout(function(){ 18 console.log(va

Javascript学习笔记:闭包题解(3)

代码: 1 function assignHandler(){ 2 var element=document.getElementById('someElementId'); 3 4 element.onclick=function(){ 5 alert(element.id); 6 } 7 } 问题:请问以上代码是否存在内存泄漏? 正确答案:存在内存泄漏 解析:由于上段代码中存在闭包,闭包会保存包含该闭包的函数的作用域链,因此element对象会一直存在于内存中,不会被垃圾回收机制回收掉.那么

Javascript学习笔记:闭包题解(1)

代码: 1 function createFunctions(){ 2 var result=[]; 3 for(var i=0;i<10;i++){ 4 result[i]=function(){ 5 return i; 6 }; 7 } 8 return result; 9 } 10 11 var funcs=createFunctions(); 12 console.log(funcs[0]()); 问题:请写出改段代码打印出的结果. 正确答案:10 解析:闭包保存的是包含函数的作用域链,