1182:食物链
总时间限制:
1000ms
内存限制:
65536kB
描述
动物王国中有三类动物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),输出假话的总数。
输入
第一行是两个整数N和K,以一个空格分隔。
以下K行每行是三个正整数 D,X,Y,两数之间用一个空格隔开,其中D表示说法的种类。
若D=1,则表示X和Y是同类。
若D=2,则表示X吃Y。
输出
只有一个整数,表示假话的数目。
样例输入
100 7 1 101 1 2 1 2 2 2 3 2 3 3 1 1 3 2 3 1 1 5 5
样例输出
3
来源
Noi 01
这道题不难看出是一道用并查集来解决的题目,是相当难的,在洛谷上面是一道提高+/省选-的题目,给大家讲一下这道题的思路吧。
思路:
刚开始,输入了n和k,然后输入3个整数,判断第一个整数是1还是2,如果是1的话,首先要判断是不是假的,怎么判断这道题是不是假的呢?第一个判断条件是如果输入的后两个数b和c其中任意一个大于了n的话,就是假话,毕竟动物编号最大才n,你大于n了,就根本没有这个动物了,第二个判断条件就是是否冲突,怎么查是否冲突呢?在这道题里面冲突的意思是,b已经可以吃c了,你把b和c放到同一类,这就明显是假话了,那这该怎么判断呢?我们可以在判断第一个数为2之后,确认了这句话不是假话,就可以进行unionSet操作,但是由于他们是b吃y,所以我们原来的unionSet函数不能实现这个要求,我们就可以重新写一个函数unionSetEat,并且多加一个结构数组*eat,里面存的是b可以吃的动物的编号,就这样,可以判断是否冲突,只需要判断eat[b的下标]是不是等于c的下标,如果是,代表之前是b吃c的,而现在竟然说b和c是同类,这明显是假话,如果以上两种都不是的话,那么就用普通的unionSet函数进行合并,表示两个动物是同一个类别。
如果输入的第一个数是2的话,先判断b是不是等于c,如果是,就代表自己吃自己,不是搞笑的吗,所以相等的话就是假话,然后判断b和c是不是同一个集合里面的(是不是同一类),如果是,就代表冲突,毕竟都是一类了,还互相吃是不可能的(根据题意),最后在判断一下,eat[c的下标]是不是等于b的下标,这个的意思是,你输入的两个数b和c代表b可以吃c,而这个判断时判断c是不是要吃b,如果是,那岂不是要互吃了,所以这句话也是假的,以上3种情况都不符合的话,那就代表说的是真话了,就运用新的函数unionSetEat函数,给两个动物做一个标记。
最后,如果输入的是假话,那么就进行sum++,最后输出sum就行了。
食物链:
#include<bits/stdc++.h>
using namespace std;
template<class T>
struct DisjointSet{
int *parent;
int *eat;
T *data;
map<T,int> m;
int capacity;
int size;
DisjointSet(int max=1000){
capacity=max;
size=0;
parent=new int[max+1];
data=new T[max+1];
eat=new T[max+1];
}
~DisjointSet(){
delete [] parent;
delete [] data;
}
bool insert(T x){
if(size==capacity) return false;
size++;
data[size]=x;
parent[size]=-1;
m[x]=size;
return true;
}
int getIndex(T x){
for(int i=1;i<=size;i++)
if(data[i]==x)
return i;
return -1;
}
int find(T x){
typename map<T,int>::iterator it;
it=m.find(x);
if(it==m.end()) return -1;
int i,rt;
i=rt=it->second;
while(parent[rt]>0)
rt=parent[rt];
return rt;
}
void unionSet(T x,T y){
int rx,ry;
rx=find(x);
ry=find(y);
if(rx==-1||ry==-1) return ;
if(rx==ry) return ;
if(parent[rx]<parent[ry]){
parent[rx]+=parent[ry];
parent[ry]=rx;
}
else{
parent[ry]+=parent[rx];
parent[rx]=ry;
}
find(x);
find(y);
}
void unionSetEat(T x,T y){
int rx,ry;
rx=find(x);
ry=find(y);
if(rx==-1||ry==-1) return ;
if(rx==ry) return ;
parent[ry]=rx;
}
};
int main(){
int n,k,sum=0;
cin>>n>>k;
while(k--){
DisjointSet<int> s;
for(int i=1;i<=n;i++) s.insert(i);
int a,b,c;
cin>>a>>b>>c;
if(b>n||c>n) sum++;
else if(a==1){
if(s.eat[s.getIndex(b)]==s.getIndex(c)) sum++;
else s.unionSet(b,c);
}
else{
if(b==c) sum++;
else if(s.find(b)==s.find(c)) sum++;
else if(s.eat[s.find(c)]) sum++;
else s.unionSet(b,c);
}
}
cout<<sum<<endl;
return 0;
}
今天的题目分享就到这里了,这道题的分析还是有些难度的!
该题链接1:
OpenJudge - 1182:食物链http://bailian.openjudge.cn/practice/1182该题链接2: