http://poj.org/problem?id=1182
Time Limit: 1000MS | Memory Limit: 10000K | |
Total Submissions: 44998 | Accepted: 13122 |
Description
现有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
以下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分析:该题是种类并查集,每个动物都属于A,B,C三类中的一类,始终满足A<-吃-B<-吃-C<-吃-A的关系;
用两个数组即可,f[]表示祖先节点,mark[]表示每个节点的状态
所以用0,1,2,三个数表示与祖先节点的关系:0表示与祖先同类,1表示被祖先吃,2表示吃祖先;
举个例子就很清楚了
设被吃者指向父节点即吃者
(1)1吃2:
A=1的祖先是1此时x=f[1]=1, mark[1]=0;
B=2的祖先是2此时y=f[2]=2], mark[2]=0;
因为1吃2,所以f[y]=x即:f[2]=1; 2的状态比1 大1,
所以mark[y]=(mark[a]+1-mark[b]+3)%3;(就是把当前的增量平移到祖先身上)
==>mark[1]=0;mark[2]=1;
(2) 2吃3;
A=2的祖先是1此时x=f[2]=1;mark[2]=1;
B=3的祖先是3此时y=f[3]=3; mark[3]=0;
f[y]=x;即:f[3]=1;
所以mark[y]=(mark[a]+1-mark[b]+3)%3;
==>mark[3]=2;
此时图形是:
(3)同理还有4吃5,5吃6可构成下图:
(4)此时加入3吃5:
A=3,x=f[3]=1;mark[3]=2;
B=5;y=f[5]=4;mark[5]=1;
3的状态要比5的状态小1,所以看看要是把5集合加入到3的集合中,5的祖先平移 量是多少mark[y]=(mark[a]+1-mark[b]+3)%3,因为mark[y]在任何时候都是mark[y]=0;所以直接赋值就行了,此时:
此时只是更新了祖先的mark值;
当找5的时候务必会进行一次finde()函数:
int finde(int x)
{
if(x!=f[x])
{
int t=f[x];
f[x]=finde(f[x]);
mark[x]=(mark[x]+mark[t])%3;
}
return f[x];
}
F[5]=1;mark[5]=0;
同理要求6的话f[6]=1;mark[6]=1;
可以看出mark值相同的动物是一类的;
加入3和5是同类的话
Mark[y]=(mark[a]-mark[b]+3)%3;
Mark[4]=1;mark[5]=2;mark[6]=0;
程序:
#include"string.h" #include"stdio.h" #include"queue" #include"stack" #include"iostream" #include"math.h" #define M 60006 #define inf 100000000 using namespace std; int f[M],mark[M]; int finde(int x) { if(x!=f[x]) { int t=f[x]; f[x]=finde(f[x]); mark[x]=(mark[x]+mark[t])%3; } return f[x]; } void make(int a,int b) { int x=finde(a); int y=finde(b); if(x!=y) { f[y]=x; mark[y]=(3+mark[a]+1-mark[b])%3; } } void same(int a,int b) { int x=finde(a); int y=finde(b); if(x!=y) { f[y]=x; mark[y]=(3+mark[a]-mark[b])%3; } } int main() { int n,m,i; scanf("%d%d",&n,&m); for(i=1;i<=n;i++) f[i]=i; memset(mark,0,sizeof(mark)); int wrong=0; while(m--) { int d,a,b; scanf("%d%d%d",&d,&a,&b); if(a>n||b>n||a<1||b<1) { wrong++; //printf("Wrong "); continue; } int x=finde(a); int y=finde(b); if(d==1) { if(x!=y) same(a,b); else { if(mark[a]!=mark[b]) { //printf("Wrong "); wrong++; } } } if(d==2) { if(x!=y) make(a,b); else { if(mark[a]+1!=mark[b]&&mark[a]!=mark[b]+2) { //printf("Wrong "); wrong++; } } } } printf("%d ",wrong); return 0; }