//7968286 vrs 1182 Accepted 548K 219MS C 1078B 2010-12-04 21:33:24
//1182 食物链 利用向量偏移的并查集
//
//每个集合都有三类动物,用rank表示,0——同类;1——食物;2——天敌,初始时都以自己为根节点,且rank为0
//只需对每一句话,判断x,y是否同一集合内,如果是就判断语句真假,否则就合并集合
//根据公式知道x-y=d-1,主要想出几个推导公式:
//1: rank[x]=(rank[x]+rank[temp])%3
// 这个最好设一两个例子验证一下,由于每次都是两集合的根合并,且只有根更改rank,所以每个x的rank值只会与
// 它的father有关(只有一次father更改,就是跟合并时),然后结合向量偏移得到。
//2: rank[rooty]=(rank[x]-rank[y]-excusion+3)%3;
//3: (rank[x]-rank[y]+3)%3!=d-1;
//三个公式都与向量偏移计算得来,看下面的详解
#include<stdio.h>
unsigned int setFather[50005];
unsigned int rank[50005];
unsigned int Find(unsigned int x)
{
unsigned int temp;
if(x==setFather[x])
return x;
temp=setFather[x];
setFather[x]=Find(setFather[x]);
//这里注意,在路径压缩过程中,由于递归时是先算根然后一直往叶子算过来,所以seFather[x]并不是原来x的父亲
rank[x]=(rank[x]+rank[temp])%3; //第一个
return setFather[x];
}
void Union(unsigned int x,unsigned int y,int excusion)
{
unsigned int rootx,rooty;
rootx=Find(x);
rooty=Find(y);
setFather[rooty]=rootx;
rank[rooty]=(rank[x]-rank[y]-excusion+3)%3; //第二个
}
int main()
{
unsigned int n,k,i;
unsigned int d,x,y;
unsigned int wrongNum;
unsigned int rootx,rooty;
scanf("%d%d",&n,&k);
wrongNum=0;
for(i=1;i<=n;i++)
{
setFather[i]=i;
rank[i]=0;
}
while(k--)
{
scanf("%d%d%d",&d,&x,&y);
if(x>n || y>n || (d==2 && x==y))
{
wrongNum++;
continue;
}
rootx=Find(x);
rooty=Find(y);
if(rootx!=rooty)
Union(x,y,d-1);
else
{
if((rank[x]-rank[y]+3)%3!=d-1) //第三个
wrongNum++;
}
}
printf("%d\n",wrongNum);
return 0;
}
//附1:
//向量偏移解说
/*
> 什么叫做向量的思维模式?
> Orz Orz
我的理解是,对于集合里的任意两个元素a,b而言,它们之间必定存在着某种联系,
因为并查集中的元素均是有联系的,否则也不会被合并到当前集合中。那么我们
就把这2个元素之间的关系量转化为一个偏移量,以食物链的关系而言,不妨假设
a->b 偏移量0时 a和b同类
a->b 偏移量1时 a吃b
a->b 偏移量2时 a被b吃,也就是b吃a
有了这些基础,我们就可以在并查集中完成任意两个元素之间的关系转换了。
不妨继续假设,a的当前集合根节点aa,b的当前集合根节点bb,a->b的偏移值为d-1(题中给出的询问已知条件)
(1)如果aa和bb不相同,那么我们把bb合并到aa上,并且更新delta[bb]值(delta[i]表示i的当前集合根节点到i的偏移量)
此时 aa->bb = aa->a + a->b + b->bb,可能这一步就是所谓向量思维模式吧
上式进一步转化为:aa->bb = (delta[a]+d-1+3-delta[b])%3 = delta[bb],(模3是保证偏移量取值始终在[0,2]间)
(2)如果aa和bb相同,那么我们就验证a->b之间的偏移量是否与题中给出的d-1一致
此时 a->b = a->aa + aa->b = a->aa + bb->b,
上式进一步转化为:a->b = (3-delta[a]+delta[b])%3,
若一致则为真,否则为假。
*/
//附2:
/*
//这是自己原来想的方法,很麻烦。。。
//用了两重并查集,结果是300个测试数据过了,3000个就WA,郁闷得要死。。。
//我的思路是每个森林都有3类ABC,假如第一次碰到动物k,就新建森林容纳他们,一旦发现他们分别属于森林且有1,或2
//的关系,就合并森林,同时把三类分别合并一起。。。
#include<stdio.h>
#include<string.h>
#define bool int
#define NONESTATES 50004
typedef struct _RELATIVE
{
unsigned int father;
unsigned int next;
unsigned int last;
int typeABC;
}RELATIVE;
RELATIVE setRelative[50005];
unsigned int setFather[50005];
bool setState[50005];
//查找在哪个森林
unsigned int Find(unsigned int x)
{
unsigned int temp,root;
root=x;
while(setFather[root]!=root)
root=setFather[root];
temp=x;
while(setFather[x]!=x)
{
temp=setFather[x];
setFather[x]=root;
x=temp;
}
return root;
}
//查找在哪个类中
unsigned int FindType(unsigned int x)
{
unsigned int temp,root;
root=x;
while(setRelative[root].father!=root)
root=setRelative[root].father;
temp=x;
while(setRelative[x].father!=x)
{
temp=setRelative[x].father;
setRelative[x].father=root;
x=temp;
}
return root;
}
//合并两个森林中的三类
//这里的操作非常复杂,我自己都看晕了,跳过吧
void UnionType(unsigned int x,unsigned int y,int type)
{
unsigned int subRootX,subRootY;
subRootX=FindType(x);
subRootY=FindType(y);
if(type==1)
{
//处理A类关系
setRelative[subRootY].father=subRootX;
//处理B类关系
if(setRelative[subRootY].next!=NONESTATES && setRelative[subRootX].next!=NONESTATES)
setRelative[ setRelative[subRootY].next ].father=setRelative[subRootX].next;
else if(setRelative[subRootY].next!=NONESTATES)
{
setRelative[ setRelative[subRootY].next ].typeABC=(setRelative[subRootX].typeABC+1)%3;
setRelative[ setRelative[subRootY].next ].last=subRootX;
setRelative[subRootX].next=setRelative[subRootY].next;
if(setRelative[subRootX].last!=NONESTATES)
setRelative[ setRelative[subRootY].next ].next=setRelative[subRootX].last;
}
//处理C类关系
if(setRelative[subRootY].last!=NONESTATES && setRelative[subRootX].last!=NONESTATES)
setRelative[ setRelative[subRootY].last ].father=setRelative[subRootX].last;
else if(setRelative[subRootY].last!=NONESTATES)
{
setRelative[ setRelative[subRootY].last ].typeABC=(setRelative[subRootX].typeABC+2)%3;
setRelative[ setRelative[subRootY].last ].next=subRootX;
setRelative[subRootX].last=setRelative[subRootY].last;
if(setRelative[subRootX].next!=NONESTATES)
setRelative[ setRelative[subRootY].last ].last=setRelative[subRootX].next;
}
}
else
{
//处理Y所在的A类关系
if(setRelative[subRootX].next!=NONESTATES)
setRelative[subRootY].father=setRelative[subRootX].next;
else
{
setRelative[subRootX].next=subRootY;
setRelative[subRootY].typeABC=(setRelative[subRootX].typeABC+1)%3;
setRelative[subRootY].last=subRootX;
if(setRelative[subRootX].last!=NONESTATES)
setRelative[subRootY].next=setRelative[subRootX].last;
}
//处理Y所在的B类关系
if(setRelative[subRootY].last!=NONESTATES)
setRelative[ setRelative[subRootY].last ].father=subRootX;
//处理Y所在的C类关系
if(setRelative[subRootX].last!=NONESTATES && setRelative[subRootY].next!=NONESTATES)
setRelative[ setRelative[subRootY].next ].father=setRelative[subRootX].last;
else if(setRelative[subRootY].next!=-1)
{
setRelative[subRootX].last=setRelative[subRootY].next;
setRelative[ setRelative[subRootY].next ].typeABC=(setRelative[subRootX].typeABC+2)%3;
setRelative[ setRelative[subRootY].next ].next=subRootX;
if(setRelative[subRootX].next!=NONESTATES)
setRelative[ setRelative[subRootY].next ].last=setRelative[subRootX].next;
}
}
}
//合并两个森林
void Union(unsigned int x,unsigned int y,int type)
{
unsigned int rootX,rootY,temp;
rootX=Find(x);
rootY=Find(y);
if(rootX>rootY)
{
temp=rootX;
rootX=rootY;
rootY=temp;
}
setFather[rootY]=rootX;
if(type==NONESTATES)
return;
UnionType(x,y,type);
}
//如果是存在第一次遇到的动物,就合并或新建一个森林
void NewSet(unsigned int x,unsigned int y,int statementType)
{
unsigned int tempRoot;
Union(x,y,NONESTATES);
if(statementType==1)
{
if(setState[y]==1)
setRelative[x].father=y;
else if(setState[x]==1)
setRelative[y].father=x;
else
{
setRelative[y].father=x;
setRelative[x].typeABC=0;
}
}
else
{
if(setState[y]==1)
{
tempRoot=FindType(y);
if(setRelative[tempRoot].last==NONESTATES)
{
setRelative[tempRoot].last=x;
setRelative[ setRelative[tempRoot].next ].next=x;
setRelative[x].typeABC=(setRelative[tempRoot].typeABC+2)%3;
setRelative[x].next=tempRoot;
setRelative[x].last=setRelative[tempRoot].next;
}
else
setRelative[x].father=setRelative[tempRoot].last;
}
else if(setState[x]==1)
{
tempRoot=FindType(x);
if(setRelative[tempRoot].next==NONESTATES)
{
setRelative[tempRoot].next=y;
setRelative[ setRelative[tempRoot].last ].last=y;
setRelative[y].typeABC=(setRelative[tempRoot].typeABC+1)%3;
setRelative[y].next=setRelative[tempRoot].last;
setRelative[y].last=tempRoot;
}
else
setRelative[y].father=setRelative[tempRoot].next;
}
else
{
setRelative[x].typeABC=0;
setRelative[x].next=y;
setRelative[y].typeABC=1;
setRelative[y].last=x;
}
}
}
//两个动物都出现过的,可以判断是真是假
bool Judge(unsigned int x,unsigned int y,int statementType)
{
if(statementType==1 && setRelative[ FindType(x) ].typeABC==setRelative[ FindType(y) ].typeABC)
return 1;
else if(statementType==2 && (setRelative[ FindType(x) ].typeABC+1)%3==setRelative[ FindType(y) ].typeABC)
return 1;
return 0;
}
int main()
{
unsigned int n,k;
unsigned int D,X,Y;
unsigned int i;
long wrongStatementNum;
scanf("%d %d",&n,&k);
memset(setState,0,sizeof(setState));
for(i=1;i<=n;i++)
{
setFather[i]=i;
setRelative[i].father=i;
setRelative[i].next=NONESTATES;
setRelative[i].last=NONESTATES;
}
wrongStatementNum=0;
while(k--)
{
scanf("%d %d %d",&D,&X,&Y);
if((X>n || Y>n) || (D==2 && X==Y))
{
wrongStatementNum++;
continue;
}
if(setState[X]==1 && setState[Y]==1)
{
if(Find(X)==Find(Y))
{
if(Judge(X,Y,D)==0)
wrongStatementNum++;
}
else
Union(X,Y,D);
}
else
{
NewSet(X,Y,D);
}
setState[X]=setState[Y]=1;
}
printf("%ld\n",wrongStatementNum);
return 0;
}
*/