您的位置:首页 > 其它

poj 1182 食物链 并查集

2015-10-25 15:04 405 查看
食物链

Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 55072 Accepted: 16149
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

Source

Noi 01

汗,在网上搜索,看了半天的带权并查集,看不懂,看了学妹的代码看懂了,

有n个动物,对于每个动物k,设food[k]=k+i,food[k+i]=k+2i;food[k+2*i]=i;这样就构成了一条循环食物链。

一共有三种动物,如果知道了两个动物是一种那么可以合并(并查集基本操作),并将他们对应的 food(被捕食者)和(捕食者)

合并。

代码上有注释,很容易看懂:

#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<climits>
#include<queue>
#include<vector>
#include<map>
#include<sstream>
#include<set>
#include<stack>
#include<cctype>
#include<utility>
#pragma comment(linker, "/STACK:102400000,102400000")
#define PI 3.1415926535897932384626
#define eps 1e-10
#define sqr(x) ((x)*(x))
#define FOR0(i,n)  for(int i=0 ;i<(n) ;i++)
#define FOR1(i,n)  for(int i=1 ;i<=(n) ;i++)
#define FORD(i,n)  for(int i=(n) ;i>=0 ;i--)
#define  lson   num<<1,le,mid
#define rson    num<<1|1,mid+1,ri
#define MID   int mid=(le+ri)>>1
#define zero(x)((x>0? x:-x)<1e-15)
#define mk    make_pair
#define _f     first
#define _s     second
using namespace std;
//const int INF=    ;
typedef long long ll;
//const ll inf =1000000000000000;//1e15;
//ifstream fin("input.txt");
//ofstream fout("output.txt");
//fin.close();
//fout.close();
//freopen("a.in","r",stdin);
//freopen("a.out","w",stdout);
const int INF =0x3f3f3f3f;
const int maxn=  50000+20   ;
//const int maxm=    ;

/*
总结:
一共有n个动物,这里虚拟出了2*n个,最后一共有3*n个,把每个动物x都虚拟出一个相应的食物food[x]和捕食它的动物food[food[x]],
这样对于x,虚拟本身出来的动物food[x]和food[food[x]]本身和x不产生矛盾,
会让我们方便解题。
*/
int pre[3*maxn],food[3*maxn];
int a[3],b[3];
int n,N,ans;//n动物种数、N虚拟的3*n种动物,ans 答案(假话数)
int find(int x)
{
return x==pre[x]? x :pre[x]=find(pre[x]);
}

void samegroup(bool same,int x,int y)//如果D==1
{//这句话说他们是同种动物
if(same)//如果两者之前的食物链合并过
{
if(a[0]!=b[0])  ans++;//如果x、y的代表动物不一样,这句话是假话。
return ;
}
for(int i=0;i<3;i++)//如果两者之前的食物链没有合并过
{
pre[b[i]]=a[i];//food和捕食者 对应合并
}

}

void different(bool same ,int x,int y)//如果D==2
{
if(x==y)  {ans++ ; return ;}//说x、y不是同种动物,但是x、y又一样,是假话
if(same)//如果两者之前的食物链合并过
{
if(a[1]!=b[0])  ans++;//这个时候x的食物代表动物a[1]应该和y的代表动物b[0]一样,否则假话
return;
}
for(int i=0;i<3;i++)
{
pre[b[i]]=a[(i+1)%3];//对应合并
}
}
void work()
{
int rela,x,y;
scanf("%d%d%d",&rela,&x,&y);
if(x>n||y>n)  {ans++;return ;}//动物序号 > 大于动物个数,假话

a[0]=find(x);a[1]=find(food[a[0]] );a[2]=find(food[a[1]] );//取x所在食物链的三个代表动物
b[0]=find(y);b[1]=find(food[b[0]] );b[2]=find(food[b[1]] );//取y所在食物链的三个代表动物
bool same=false;
for(int j=0;j<3;j++)
{
if(a[0]==b[j]) {same=1;break;}
}//如果same==1,那么证明x、y所在的食物链已经合并过了,只需要判断真假
if(rela==1)   samegroup(same,x,y);//如果D==1

else     different(same,x,y);//如果D==2

}

void init()
{
ans=0;
for(int i=1;i<=N;i++)
pre[i]=i;
for(int i=1;i<=n;i++)
{
food[i]=i+n;
food[i+n]=i+2*n;
food[i+2*n]=i;
}
}
int main()
{
int k;
scanf("%d%d",&n,&k);

N=3*n;
init();
for(int i=1;i<=k;i++)
{

work();
}
printf("%d\n",ans);

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: