并查集
(Union/Find)主要涉及两种基本操作:合并和查找。
基本操作位合并之后,为查找某两个元素是否已经处在同一个集合中,需要Find操作。
我们可以将并查集的元素看成是一个树,也可以看成是一个集合
int mset[maxn];
//初始化
void init()
{
for(int i=0;i<=15000;i++)mset[i]=i;
}
//find函数,常常可以在查找根结点时来修改一些量的值
int find(int a)
{
return mset[a]==a?a:find(mset[a]);
}
//合并操作,在合并时可以进行需要合并过程中的一些操作
void uni(int a,int b)
{
a=find(a);b=find(b);
if(a<b)mset[b]=a; //可添加其他操作
if(b<a)mset[a]=b;
}
用途:
1.处理联通分支,联通量相关问题。比方说给定一个堆独立的树(即森林),对森林中的许多树进行union操作之后,整个树的个数将会减少,现在我们将每个树中的信息维护到每个树的根结点,利用根结点来处理整个树以至于整个森林中的信息。
这个题就是每个卡牌相当于一条边,卡牌的重叠就是边的连接,最后形成联通分支。在每个联通分支中维护行边和列边的个数,最后统计每个树中行列个数更多的那一个求和,将所有信息维护到每个树的根结点中。
//结构过程示例
int main()
{
init(); //初始化
for(int i=1;i<=n;i++) //合并边 并在合并过程中处理各种信息
uni();
for(int i=1;i<=n;i++) //获得每个树中信息
if(mset[i]==i)
getresult();
}
带权并查集
带权并查集,即对于同一个并查集中的多个元素之间存在另外一种置换群关系
//关系种类数 记得修改
int relmod=2;
//并查集类
struct Mset
{
int fa;
int rel;
};
Mset mset[maxn];
//并查集初始化
void init()
{
for(int i=0;i<=n;i++)
{
mset[i].fa=i;
mset[i].rel=0;
}
}
//查找a的根结点,并路径压缩,同时修改对父亲的rel
int find(int a)
{
if(mset[a].fa==a)
return a;
int root=find(mset[a].fa); //查找父亲
mset[a].rel=(mset[a].rel+mset[mset[a].fa].rel)%relmod; //修改对关系
return mset[a].fa=root; //修改父亲
}
//合并子集,基于向量的关系
void uni(int a,int b,int rel)
{
int roota=find(a);
int rootb=find(b);
if(roota<rootb)
{
mset[rootb].fa=roota;
mset[rootb].rel=(relmod+relmod-mset[b].rel-rel+mset[a].rel)%relmod;
}
else
{
mset[roota].fa=rootb;
mset[roota].rel=(relmod-mset[a].rel+rel+mset[b].rel)%relmod;
}
}
//查找同一棵树中的两种个元素之间a->b的关系
int getrel(int a,int b)
{
int roota=find(a);
int rootb=find(b);
if(roota!=rootb)
return -1;
return (mset[a].rel+relmod-mset[b].rel)%relmod;
}