还是放个链接吧
题解
一坨废话
写这题是因为看着说是带权并查集,然后想想当初第一次学并查集的时候考试上似乎也碰上一个带权并查集然后被我难得的手推A掉了,就来试试这题看看我的假脑子会不会忘掉曾经会做的模型。
首先,开头的ABC有什么存在的意义啊(这样说造成的后果在后面)!然后…这根本不是我认识的带权并查集啊喂!不应该是根据权值的优先级来合并的那种么(虽然是什么题我也忘了)!
看着像是要维护一个友好关系和敌对关系,这不是2-SAT么,但我不会打呀!先手推一下…好复杂的样子还是看dalaoKB博客吧,顺便提高一下2-SAT的姿势水平。
正式开始
… ……….. ……………….. (看着博客,沉默,拍头)
这就是它带的权啊,ABC是有用的啊
首先因为动物总共只有三种(从这里就开始错了),还是可以用一种方式表示的(被吃就看作吃两次,因为总共就三种)所以关系只有三种。
然后我们知道,如果我们知道a和b的关系,b和c的关系,那么我们就一定知道a和c的关系。这就是用并查集维护的原因啦。
那么我们将两个点之间(a与b),a和b同类为0,a吃b看作1,b被a吃就看作2*(我知道网上其他博客都是a被b吃看作1的,但我中文思维严重,一定要这样…)*
啥?你问我这个时候我们也知道了b和c的关系,具体怎么知道a和c的关系?加起来取模3啊,就行了。画个蛇咬尾式的图就知道了
那么我们的工作就是,在处理命令的时候若a与c都关于一个共同的b有关,那么就判错
1的时候a和c(与b的关系值,即上面的权值)是不是确实模3相等啊
2的时候a和c是不是确实是a吃c(a的关系值加一模三是否与c与b的关系值相等)而不是其它两种情况呢 因为我自己被这些加粗坑了
并没有共同b的时候,先将祖先处理,再根据应满足的关系式移项赋值,这个地方可能会有人像我一样有个假脑子,看着别人的赋值一下子不知道怎么回事吧
接下来默认a接到c下面
1的时候要求a和c要是同类那么就是 a到c的祖先的总权值(总权值即a到祖先的总权值加上a的祖先到c的祖先的总权值 记得模三)要等于c到祖先的权值 (此时c的祖先就是对于a和c来说的b了)。用dx表示x到祖先的权值,用式子表示为
具体实现需要注意小于零的情况
2的时候 根据我的中文思维式子就和大部分不一样了,类比也可得出
代码
#include <cstdio>
const int maxn = 5e4 + 10;
int f[maxn], d[maxn];
int Find(int x){
if (f[x] == x) return x;
int fa = f[x];
f[x] = Find(f[x]); d[x] = (d[fa] + d[x]) % 3;
return f[x];
}
int main (){
int n, k; scanf ("%d%d", &n, &k);
for (int i = 1; i <= n; ++i) f[i] = i;
int ans = 0;
for (int i = 1, fl, X, Y, rx, ry; i <= k; ++i){
scanf ("%d%d%d", &fl, &X, &Y);
if (X > n || Y > n){++ans; continue;}
if (X == Y){if (fl == 2) ++ans; continue;}
rx = Find(X), ry = Find(Y);
if (rx == ry){
if (fl == 1 && d[X] != d[Y]) ++ans;
if (fl == 2 && (d[X] + 1) % 3 != d[Y]) ++ans;
}
else{
f[rx] = ry;
if (fl == 1) d[rx] = (d[Y] - d[X] + 3) % 3;
else d[rx] = (d[Y] - d[X] + 2) % 3;
}
}
printf ("%d", ans);
return 0;
}