codevs1074 食物链(并查集+向量偏移)
动物王国中有三类动物 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; }
版权声明:本文为博主原创文章,未经博主允许不得转载。