好久没写博客了,差点忘了在哪儿写,不写博客是真的对学过的东西一点印象都没有T_T;
丧心病狂的大佬挂了一道种类并查集+dp的题,然而我却连种类并查集是什么都不知道,赶紧学习缩小差距;
1.带权并查集(对这两个的关系还有点儿懵,先上再说)
就像名字一样,带权并查集就是要多维护一个权值数组;和普通的并查集大同小异,用f[]数组存父节点,val存当前结点到父亲结点的权值,
首先是初始化
void Initial()
{
for(int i=1;i<=n;i++) //n是总数啦
{
f[i]=i;val[i]=0;
}
}
先来不状态压缩的没有压缩时可以知道f[3]=2,f[2]=1,f[1]=1;val[3]=4;val[2]=3;val[1]=0;
状态压缩后:f[3]=1;val[3]=7;其他不变;所以可以得出代码如下
int Find(int x)
{
if(f[x]==x)
return f[x];
int tmp=Find(f[x]); //这里不能够没有,因为在递归的过程中f[x]的值会被改变,会影响val[x]的更新
val[x]=val[x]+val[f[x]];
return f[x]=tmp;
}
而对于合并两个集合,是将元素间关系看成向量
我们可以列出向量图
我们假设上图中2->1表示1比2大3,同理得其他的,一开始1,2处于一个集合,3,处于另一个集合,现在要将这两个集合合并,那么1->4得权值就可以得出,根据平行四边形法则,val[1]=(-val[2]+v+val[3])=8(其中v表示3比2大多少有,也就是2->3),就是说4比1大8,由此我们便得到了如何将两个带权集合合并;
void Union(int a,int b)
{
int fa=Find(a),fb=Find(b);
if(fa!=fb)
{
f[fa]=fb;
val[fa]=-val[a]+v+val[b];
}
} //带权并查集Over
例题:How Many Answers Are Wrong HDU - 3038
2.种类并查集
我觉得种类并查集延用了带权并查集的特性,还是相当于维护了一个权值,只不过这个权值带有一定的意义,拿例题“食物链”来说,我们指定x->y表示与y是x的父节点,即father[x]=y;我们用rela[i]来表示节点i和它的父节点之间的关系,规定rela[x]=0表示x,y是同类,rela[x]=1表示y吃x,rela[x]=2表示x吃y,那么我们可以根据这些权值推断出两个结点之间实际的关系。
比如现在告诉你3->1的值为1(1吃3),而2->1的值为2(2吃1),那么我们根据题意就可以得出3吃2,即2->3为1,也就得到了上图的关系。假设现在再来一句话说2吃3那么就是错的。问题是如何得到这样的关系。对于给定的x,y和它们之间的关系,我们分两种情况
1.假设father[x]==father[y],就是说它们是一个集合的,现在的任务就是把它们推出它们之间的关系,也就是上图的情况,1为父节点,先开始的条件有3->1的为1,2->1=2,如何得到2->3=1呢?同样我们可以用向量的方法:2->3=2->1 - 1->3(添负号,箭头反向)=2-1=1;这样我们得到了2->3的值为1(这里在想一下,如果我们想得到假设之前我们已经建立了2->1的关系,现在又给出2->1的关系,如何得到到给出的关系对不对呢;我们只需要把上图的3换成1就可以了,同时3->1=1就变成了1->1=0同样的方法)
2,假设father[x]!=father[y],x,y不在同一个集合也就是要联合x,y两个集合,已知y->x=1,x->fx=1,y->fy=0
同样的用向量就可以了fy->fx=-(y->fy)+y->x+x->fx=1+1=2;(已知fx吃x,x吃y,y和fy是同类,所以由题意可知fy吃fx),这样我们就可以将两个集合联合在一起并且维护好关系。
值得注意的是,这些操作对向量方向的要求很高,不能弄错方向,比如联合两个集合的时候,我们求的是fy->fx,也就是fy是儿子,fx是父亲,那么我们就应该father[fy]=fx;而不是father[fx]=fy;同样我们求的关系是rela[fy]的值,而不是rela[fx],如果箭头反向,这些操作都要变.
写的差不多了,其实我对种类并查集的种类还是不怎么明白,但如果这样想一想:子节点和父节点的权值为0表示两者同类,为1表示两者异类,不就可以判断两个结点是不是同一类了的吗,权值的取值更多也就代表结点之间的关系更加的多样化,这不就有点儿种类的味道了嘛(我们的前辈们真是有点儿东西的啊^ _ ^);
食物链
#include <iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#define lson (rt<<1)
#define rson (rt<<1|1)
const int inf_max=0x3f3f3f;
const int maxn = 100000+10;
typedef long long ll;
using namespace std;
int n,k,father[maxn],rela[maxn];
int Find(int x)
{
if(x==father[x])
return father[x];
int tmp=Find(father[x]);
rela[x]=(rela[x]+rela[father[x]])%3;
return father[x]=tmp; //状态压缩
}
void Initial()
{
for(int i=0;i<=n;i++)
{
father[i]=i;
rela[i]=0;
}
}
int main()
{
scanf("%d%d",&n,&k);
Initial();
int ans=0;
for(int i=1;i<=k;i++)
{
int re,x,y;
scanf("%d%d%d",&re,&x,&y);
if((re==2&&x==y)||x>n||y>n)
{
//printf("this is lie\n");
ans++;
continue;
}
re--;
int fx=Find(x),fy=Find(y);
if(fx!=fy)
{
father[fx]=fy;
rela[fx]=(-rela[x]+rela[y]-re+3)%3;
}
else if((rela[y]-rela[x]+3)%3!=re)
{
//printf("rela[%d]=%d,rela[%d]=%d,this is lie\n",x,rela[x],y,rela[y]);
ans++;
}
}
// for(int i=1;i<=3;i++)
// printf("father[%d]=%d\n",i,father[i]);
printf("%d\n",ans);
return 0;
}