【蓝桥杯每日一题】4.9网络分析(代码详解版)

终于把清明节假期时自己挖的坑给补上了

题目来源:

2069. 网络分析 - AcWing题库

参考:

Bear_king的图解
y总代码解读

思路1:

思考:

  • 题目看着,看到“发送信息后,又会发送到相邻的结点上面”这句话,觉得有点并查集的味道,但是后面又说:“计算出每个节点存储信息的大小”,有些复杂!
  • 按照本人理解,并查集是类似于树状数组的集合,但是没有权值,会不会有带权并查集呢?不妨利用数组 d d d来存放并查集中的每个元素所代表的结点权值。
  • 依照题意,输入 1 1 1时,两个结点连接,也即“认大哥”,输入 2 2 2时,发送消息

思路有了,先暴力试一下呗~

暴力代码:(7/10)

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,m;
int num[N];
int a,x,y;
int fa[N];
int Find(int x){
    if(x!=fa[x]) fa[x]=Find(fa[x]);
     return fa[x];
}
void join(int x,int y){
    int a=Find(x);
    int b=Find(y);
    if(a!=b){
        fa[a]=b;
    }
}
int main(int argc, char** argv) {
    for(int i=0;i<100005;i++){
        fa[i]=i;
    }
    scanf("%d%d",&n,&m);
    while(m--){
        scanf("%d%d%d",&a,&x,&y);
        if(a==1){
            join(x,y);
        }else{
            int t=Find(x);
            for(int i=1;i<=n;i++){
                if(t==Find(i)){
                    num[i]+=y;  
                }
            }
        }
    }
    for(int i=1;i<=n;i++){
        printf("%d ",num[i]);
    }
        return 0;
}

思路2:

思路1无法AC,百思不得其解,看了讲解视频才了解到树形差分(其实之前学差分、二维差分的时候,我也看到过树形差分,但是没有掌握……)

思路:

  • 并查集,通过增加根节点的值,表示整棵树所有下面的结点都加上这个值
  • 对于操作1,不能直接将 a a a树连到 b b b树上,需要将a结点减去b结点的权值,才不会导致错误
  • 并查集操作时,如果需要把 b b b到根的路径压缩,数学归纳法可以证明, d [ b ] d[b] d[b]应更改为 d [ p [ b ] ] + d [ b ] d[p[b]]+d[b] d[p[b]]+d[b]
  • 很考察对并查集的理解!每次调用 f i n d ( a ) find(a) find(a)函数,会保证 a a a要么是父结点,要么它的父结点是根结点(第三层以下的结点更新后连到第二层)
  • 什么叫树上差分?每个结点的实际值是它走到根节点的权值之和
    但是这样操作只保证了第一第二层的结点是被维护更新好了的,如果要输出哪个点的权值,需要先对这个点 f i n d find find一下,保证更新好

AC代码:

//树上差分+并查集  这就像一颗树
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;

int n,m;
int a,x,y;
int d[N]; //记录每个结点的权值
int fa[N];  //并查集数组


int find(int x)
//根和根的孩子是保证维护好了的,但是第三层往后的需要更新,用上面写的数学归纳法思路
//也就是如果x在第三层及以下,这里需要把它的权值处理好之后连到第二层!
{
	//路径压缩
	//如果这个结点或者它的父结点就是根节点,则不用修正操作
	if(x==fa[x]||fa[fa[x]]==fa[x]) return fa[x];
	
	int r=find(fa[x]);//先将它的父结点处理完,并合并
	d[x]+=d[fa[x]];
	fa[x]=r;//前面处理到了根节点,这里可以直接将x结点连上去了
	return r; 
}

void join(int x,int y){
	int a=find(x);
	int b=find(y);
	if(a!=b)
	{
		d[a]-=d[b]; //将a根直接连接到b根上,维护a结点的信息
		fa[a]=b;
	}
}

int main()
{
	for(int i=0;i<N;i++) fa[i]=i; //并查集初始化!
	
	scanf("%d%d",&n,&m);
	
	while(m--)
	{
		scanf("%d%d%d",&a,&x,&y);
		if(a==1){
			join(x,y);
		}
		else{
			x=find(x);
			d[x]+=y; //根节点+b
		}
	}
	
	for(int i=1;i<=n;i++)
	{
		if(i==find(i)) //本身是根节点
		{
			printf("%d",d[i]); 
		}
		//因为前面调用过了find(i),这里可以保证i是第二层的结点,即保证它的值被正确更新过
		else{
			printf("%d ",d[i]+d[find(i)]);// 权值=当前结点 + 到根结点的权值
		}
	}
	return 0;
}

结束语:
蓝桥杯更完后续会考虑每周整理计算机四大件(DS,OS,CO,CN)的思维导图或者笔记(如果有笔记的话)

  • 13
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值