动物王国中有三类动物 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
输入文件
对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\n",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\n",ans);
return 0;
}