思路
-
1.
-
2.图.
-
1.当某点到根节点距离为1
-
根节点吃该生物
-
-
2.当某点到根节点距离为2
-
距离为1的点吃距离为2的点
-
-
3.当某点到根节点距离为3
-
距离为2的点吃根节点
-
-
插入点到根节点的距离X
-
X%3==0 该点是根节点
-
X%3==1 该点吃根节点
-
X%3==2 该点被根节点吃
-
-
距离
-
设X是第0代
-
Y吃X 则 Y为第一代
-
Z吃Y 则Z为第二代
-
Q吃Z 则Q为第三代
-
W吃Q 则W为第四代
-
.....
-
M吃N 则M为第n代
-
-
-
3.并查集
-
维护
-
操作前每个点指向其父节点
-
每个点到其父节点距离为1
-
在进行路径压缩后每个点直接指向根节点
-
我们在进行路径压缩的同时更新每个点到根节点的距离
-
-
-
变量说明(服务下if说明)
-
t 说法类型
-
1.x 和y是同类
-
2.x吃y
-
-
x 动物的编号
-
y 动物的编号
-
n 动物的数量
-
不符号条件的加粗
-
-
if的分类说明
-
1.当输入的编号x/y>n时
-
if(x>n||y>n)
-
-
2.当输入的编x/y<=n时
-
else
-
-
2.1当t==1
-
if(t==1)
-
-
2.2当t==2
-
else
-
-
2.1.1当px == py && d[x] % 3 != d[y] % 3
-
if(px == py && d[x] % 3 != d[y] % 3)
-
-
2.1.2当px!=py
-
else if(px!=py)
-
-
2.2.1 当px == py && (d[x] - d[y] - 1) % 3
-
if (px == py && (d[x] - d[y] - 1) % 3)
-
-
2.2.2当px!=py
-
else if (px != py)
-
-
#include<iostream>
using namespace std;
const int N = 5e4 + 10;
int n,m;//n为动物数,m为语句数
int p[N],d[N];
//p是 父 d是距离
/*
并查集
1.子指向自己的父
在使用find一次后全部指向根节点
查找结点,由子节点开始向父-->爷-->根
p[i]存放i的父结点的下标更新后存放i的根结点的下标
d[i]存放i到父的层数,find一次后更新为到根的层数
只有根节点自己等于自己
*/
int find(int x)
{
if (p[x] != x)
{
/*
* 因为find是递归函数,本题中的find出的时候代表p[x]=x,则这时候每出一层
* 会将一个p[]结点赋值为其根结点
find(p[x])用作子节点走向根节点
return p[x]用于取得根节点后,给为找到根结点而走过的子孙结点赋值为根结点的值
p[x]=find(p[x])当赋值时会因为递归导致这时候的find(p[x])是找到根节点后的,
因此存的都是根节点赋值赋入的都是根节点,即此时的p[x]存的是根节点
概况为:p[x]=find(p[x]) 后p[x]存的都是根节点
但
d[]的目标d[子到父]+d[父到爷]+d[爷到祖爷]+..+d[..到根]
我们要找的是每一步到每一步的的父节点的长度
*/
int t = find(p[x]);
d[x] += d[p[x]];//这时候我们不调用find赋值根结点每一个不赋值不改变
p[x] = t;
//子存父 子存爷 子存祖爷..子存根
//p[x]=findp[x]//将父系列赋给子逐层增加
//d[x]+=d[p[x]]加这时候父结点存储的到父的父结点的位置
}
return p[x];
}
int main()
{
int res;//记录假话数
int t, x, y;//z选项 x y
scanf("%d%d",&n,&m);
for (int i = 1; i <= n; i++) p[i] = i;//初始化
while (m--)
{
scanf("%d%d%d", &t, &x, &y);
if (x > n || y > n)res++;
else
{
int px=find(x),py=find(y);
//find()=更新每棵树的结点存储的信息(更新为根节点)
if (t==1)
{
//p[x]=p[y]代表此时根是同一个,因为赋值是自下而上 最终根可能值同,但是p[]不同
//if(px==py&&d[x] % 3 != d[y] % 3 )res++;
//d[x] % 3 != d[y] % 3 -->(d[x]-d[y])%3 !=0 --> (d[x]-d[y])%3 =1/2=true
//if (px == py && d[x] % 3 != d[y] % 3)res++;
/*补充针对(d[x] - d[y]) % 3 不能换为 d[x] % 3 != d[y] % 3
如果d[x]和d[y]一正一负的话!=的判断方法就是错误的,比如说d[x] = -1,d[y] = 2,
两者都是根节点的猎物,(d[x] - d[y]) % 3 == 0, 但是d[x] % 3 = -1, d[y] % 3 = 2,
如果实在想判断两者余数相等,可以改成(d[x] % 3 + 3) % 3 != (d[y] % 3 + 3) % 3,
这样就消除了负数的影响。这个问题归根结底是计算机取模计算余数是没有正负限制的,
而我们数学一般规定余数为非负数。
*/
if (px == py && (d[x] - d[y]) % 3) res++;
else if (px != py)
{
/* xy不在一个集合里, 先进行集合拼接在进行判断
p[x]=find(p[y]) 让x指向y
此时因为d[p[x]]的长度我们并不知道,因此需要算
我们需要满足的条件 x y是同类
1. d[x新]=d[x]+? d[y]=d[y]
2. d[x新]%3=d[y]%3 同时去取余
d[x]+?=d[y]
?=d[y]-d[x]
*/
p[px] = py;//y的根值赋给x 因为这时候p[]存的全是根值
//p[find(x)]=find(y);等同于
d[px] = d[y] - d[x];//更新原x的根到y的根的距离,因为存的就是当前点到根的距离
}else
{
/*
d[x] % 3 == 0;根节点
d[x] % 3 == 1;吃根节点
d[x] % 3 == 2;被根节点吃
A-->B A到B的肚子里
t=d[x]%3
0-->1
1-->2
2-->0
条件:x吃y
y-->x
0-->1
1-->2
2-->0
即为%3前x比y大1
正确的d[x]-d[y]=1
(d[x]-d[y]-1)%3=0
不正确的(d[x]-d[y]-1)%3=1
*/
if (px == py && (d[x] - d[y] - 1) % 3)res++;
else if (px != py)
{
/*
1. d[x新]=d[x]+? d[y]=d[y]
2. d[x新]-d[y]=1
d[x]+?-d[y]=1
?=1+d[y]-d[x]
*/
p[px] = py;//拼接
d[px] = 1 + d[y] - d[x];
}
}
}
}
}
printf("%d\n",res);
return 0;
}
//------------------------------------------------
#include<iostream>
using namespace std;
const int N = 5e4 + 10;
int n, m;
int p[N], d[N];
int find(int x)
{
if (p[x] != x)
{
int t = find(p[x]);
d[x] += d[p[x]];
p[x] = t;
}
return p[x];
}
int main()
{
int res = 0;
int t, x, y;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) p[i] = i;
while (m--)
{
scanf("%d%d%d", &t, &x, &y);
if (x > n || y > n) res++;
else
{
int px = find(x), py = find(y);
if (t == 1)
{
//if (px == py && d[x] % 3 != d[y] % 3) res++;
if (px == py && (d[x] - d[y]) % 3) res++;
else if (px != py)
{
p[px] = py;
d[px] = d[y] - d[x];
}
}
else
{
if (px == py && (d[x] - d[y] - 1) % 3) res++;
else if (px != py)
{
p[px] = py;
d[px] = 1 + d[y] - d[x];
}
}
}
}
printf("%d\n", res);
return 0;
}