呐呐!小白的第一篇博客就从今天开始写吧。先从一道简简单单的模板题开始吧,感觉应该没多少人看,会不会显得我废话太多了啊...咳咳,废话不多说,来看看题目吧。/*如果觉得废话太多,建议直接跳到文章结尾看看总结*/
今天又是学习并查集的一天。题目如下。
学长毕业啦!
学长自己开了一家大公司,现在很多公司选择了联盟。所以当决定和一家公司竞争的时候就要和联盟内所有公司竞争。
盟友关系是有传递性的。即 A 和 B 成为盟友,那么就会和 B 现有的其他盟友也成为盟友。
每个公司都有势力值。而联盟的势力值是所有联盟内公司的总和。
输入格式
第一行有两个数 n和 m。1<=n,m<=1e5
第二行有 n 个整数,代表每个公司的势力值,不超过 1e3。
以后 m 行代表 m 个事件:
1 x y 代表公司 x 和公司 y 结盟。
2 x 代表学长询问公司 x 所在的联盟的势力值。
输出格式
对于每一次询问输出一行,为所在的联盟的势力值。
Sample Input
5 5 1 2 3 4 5 1 1 2 2 2 2 5 1 2 3 2 1Sample Output
3 5 6
首先,这是一道并查集的模板题(感觉自己好像在说废话,这不重要!!!)。那么这道题的解题关键就在于查找和合并。但是这道题目又在最基础的并查集的上面增加了一个势力值,按照之前看过的《啊哈》里面的例子来说,就相当于,擒贼擒王的时候,每个强盗又带着自己的身家,我们每一次询问,询问的是这个强盗团伙总共的身家。而不是问他们各自的家产。
那么这道题目就应该是(找+合并/身家的合并)
并查集最基本的三个函数肯定不能少(初始化函数+查找+合并函数)
void init()//初始化数组的一个函数
{
int i;
for(i=1;i<=n;i++){
parent[i]=i;
}
}
int find(int x)//查找并查集的老祖宗,盗贼的大头头的函数
{
if(parent[x]==x){
return x;
}else {
parent[x]=find(parent[x]);
return parent[x];
}
}
void un(int x,int y)//把几个集合合并到一起,几个盗贼扔进一窝的函数
{
int dx=find(x);
int dy=find(y);
if(dx!=dy)
{
parent[dy]=dx;
}
}
写到这里,这道题目的大致结构已经确定了,那么接下来,就是一些细节。
就如同我在题目上用蓝色的字体标注出来的那样。他的关键在于势力值的叠加应该放在哪里。我最开始的时候一直WA,甚至是前两个询问样例过了,但是最后一个没过。我一度认为是题目出错了仔细辨认后发现是我的势力值叠加放错位置了。我可真是个小聪明。那么,势力值的叠加应该放在哪里呢?应该放在合并的函数里而不是放在查找的函数。如果你放在查找的函数里,那你肯定WA。什么?你不信?那你把第一组询问样例后面加一个2 1,答案肯定是1。
所以,正解代码是这样的。/*在联合的同时更改了最高头目的小钱钱,按照《啊哈》里面的例子,就是dy归属了dx,同时dx的势力值得到了增加*/
void uni(int x,int y)
{
int dx=find(x);
int dy=find(y);
if(dx!=dy)
{
parent[dy]=dx;
v[dx]+=v[dy];/*改变归属的同时,把势力值叠加 ,类似于指向,
或者说是归属,parent[dy]=dx就是dy指向了dx。*/
}
}
你是不是觉得到这里就结束了?肯定还没有哦!不过写到这里就差不多七七八八了,还差一个点睛之笔/*hhh也不能这么讲,毕竟如果你没有这最后一步,可能只能过一个样例。*/
if(a==2){
scanf("%d",&k);
j=find(k);//寻找那个幕后真正的势力。
printf("%d\n",v[j]);
}
最后这一步呢,就是当你想要询问k所属联盟的势力值,你就要找到k所归属的联盟头头手里的势力值,也就是他的老祖宗。比如说,现在有五个集团1 2 3 4 5分别是他们的势力值。1 2进行了联合,那么联盟现在的势力值就是3。这个时候你要询问2很显然他的势力值是3。那么这个3是怎么来的呢?现在的parent[2]=1,而v[1]=3,但是v[2]=2,但是2归属于1/*按照“靠左”规则*/,所以我们要找到1,而parent[1]=1,所以这个1就是我们要找的头头,而他手里的势力值就是我们要问的联盟势力值。
好了,说到这里,稍微总结一下吧。题目给出我们n个公司,进行了m个事件,1xx就是联盟,2x就是询问势力值,联盟的时候我们要把他们的势力值进行叠加,公司之间进行链接。而询问的时候我们要写出他们的势力值。就是这样的一个大致思路。而对于势力值,我的思路是,将所有的势力值都收归与联盟的几家公司中的其中一个,任意都可,不一定非要是1,但是为了方便,我就按照左边的那个公司来算。然后不改变子公司的势力,把所有的势力叠加到总公司。最后,询问的时候再查找背后的总公司,然后回答出总公司的势力,就是整个联盟的势力值。应该就是这样。
好啦,这就是我对于这道题的理解以及解题思路。虽然是道简单的板子题,而且我感觉我废话了好多,但是www还是希望能对大家有哪怕一丁点儿的帮助啊。如果哪里有问题的话还请大佬指正。这篇题解就到这里啦!咱这个蒟蒻还请各位多多指点!!!!!有什么问题的话还请私信或者评论都行。
差点忘了,完整代码附上!分析在上面,我就不写注释了哈。
#include<bits/stdc++.h>
#define N 110000
using namespace std;
int parent[N],n,m,v[N],k,a,b,c,j;
void init()
{
for(int i=1;i<=n;i++)
{
parent[i]=i;
}
}
int find(int x)
{
if(parent[x]==x)
{
return x;
}else {
parent[x]=find(parent[x]);
return parent[x];
}
}
void uni(int x,int y)
{
int dx=find(x);
int dy=find(y);
if(dx!=dy)
{
parent[dy]=dx;
v[dx]+=v[dy];
}
}
int main()
{
scanf("%d %d",&n,&m);
init();
for(int i=1;i<=n;i++){
scanf("%d",&v[i]);
}while(m--){
scanf("%d",&a);
if(a==1){
scanf("%d %d",&b,&c);
uni(b,c);
}else if(a==2){
scanf("%d",&k);
j=find(k);
printf("%d\n",v[j]);
}
}return 0;
}