题意:m次查询,每次查询都只有两种格式:
1 x y表示x与y是同类;
2 x y 表示x吃y;
问其中有多少句假话。
1.当x,y中某个标号大于n时,直接判假;
2.判断矛盾:这里有一个很好用的方法:就是用数学中的向量去看待每一条边权,并规定方向,这样,通过向量的加减运算就可以很直观的得出之间的权值关系了(手动画图很清晰)。由于这个链是一个循环链也就是说数值之间的变化必然会循环在某些数字之中;
设关系(y->x):0为x与y同类;1为x吃y,2为x被y吃(两个元素之间仅存在这些可能的关系)当然数字的选择还是有一些意义的,这样的话:每次输入的d-1就是表示x与y的关系了。(这里是三种关系的循环,故每次运算要%3保持在这个关系圈内)
对于关系转移:
(1)并查集路径压缩中:当父节点变化时通过递归更新每个父节点的权值,最后通过子节点与父节点之间的向量(变量:子,父,父父:三角形法则)加法(要取模保证关系循环!)更新子节点的权值。
(2)合并中:这时出现了四个变量(x,fx,y,fy,四边形中进行向量加法运算),注意点:合并时一定要每次都是合并到同一棵树上。
代码如下:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define maxn 50050
//关系中权值为0表示两者为同类;
//a->b权值为1表示a吃b;
//a->b权值为2表示a被b吃,即b吃a;
//用向量加减法进行关系之间的运算处理,手动画图;
int f[maxn],dis[maxn];//定义父亲数组,当前节点与根节点之间的权值数组;
int n,k;
int ans;
inline void ini()
{
for(int i=1;i<=n;i++)
{
f[i]=i;
dis[i]=0;
}
ans=0;
}
int find(int x)
{
if(x==f[x])
{
return x;
}
else
{
int father=f[x];
f[x]=find(f[x]);
dis[x]=(dis[x]+dis[father])%3;
}
return f[x];
}
void connect(int a,int b,int d)
{
int fa=find(a);
int fb=find(b);
if(fa==fb && ((dis[a]+d-1+3)%3)!=dis[b])
{
ans++;
}
else
if(fa!=fb)
{
f[fa]=fb;
dis[fa]=(dis[b]-d+1-dis[a]+3)%3;
}
}
int main()
{
int i,j;
int d,a,b;
scanf("%d%d",&n,&k);
ini();
for(i=1;i<=k;i++)
{
scanf("%d%d%d",&d,&a,&b);
if(a>n||b>n)
{
ans++;
}
else
{
connect(a,b,d);
}
}
printf("%d\n",ans);
return 0;
}