[蓝桥杯][2020年第十一届真题第一场]网络分析

题目描述:

小明正在做一个网络实验。

他设置了 n 台电脑,称为节点,用于收发和存储数据。

初始时,所有节点都是独立的,不存在任何连接。

小明可以通过网线将两个节点连接起来,连接后两个节点就可以互相通信了。

两个节点如果存在网线连接,称为相邻。

小明有时会测试当时的网络,他会在某个节点发送一条信息,信息会发送到每个相邻的节点,之后这些节点又会转发到自己相邻的节点,直到所有直接或间接相邻的节点都收到了信息。

所有发送和接收的节点都会将信息存储下来。

一条信息只存储一次。

给出小明连接和测试的过程,请计算出每个节点存储信息的大小。

输入格式
输入的第一行包含两个整数 n,m,分别表示节点数量和操作数量。

节点从 1 至 n 编号。

接下来 m 行,每行三个整数,表示一个操作。

如果操作为 1 a b,表示将节点 a 和节点 b 通过网线连接起来。当 a = b 时,表示连接了一个自环,对网络没有实质影响。
如果操作为 2 p t,表示在节点 p 上发送一条大小为 t 的信息。

输出格式
输出一行,包含 n 个整数,相邻整数之间用一个空格分割,依次表示进行完上述操作后节点 1 至节点 n 上存储信息的大小。

数据范围
1≤n≤10000,
1≤m≤105,
1≤t≤100

输入样例14 8
1 1 2
2 1 10
2 3 5
1 4 1
2 2 2
1 1 2
1 2 4
2 2 1

输出样例113 13 5 3

解题思路:

我们通过并查集合并连通块,保证同一个连通块内的点同属一个集合

对于每一个合并操作,找到两个点所属的集合

如果这两个点不在同一连通块,那么我们构造一个新点,使这个新点成为集合合并后的根节点

对于每次连通块累加操作,我们只需要向集合的根节点累加一个值即可
最后对我们所构造出来的一堆树DP(只是遍历一下),把每个点的权值下放到子树中的所有节点中

然后依次输出编号为 1−n的节点的权值即可


#include<bits/stdc++.h>
#define x first
#define y second
#define mem1(h) memset(h,-1,sizeof h)
#define mem0(h) memset(h,0,sizeof h)
#define mcp(a,b) memcpy(a,b,sizeof b)
using namespace std;
typedef long long LL;
typedef unsigned long long ull; 
typedef pair<int,int>PII;
typedef pair<double,double>PDD;
namespace IO{
    
    
	inline LL read(){
    
    
		LL o=0,f=1;char c=getchar();
		while(c<'0'||c>'9'){
    
    if(c=='-')f=-1;c=getchar();}
		while(c>='0'&&c<='9'){
    
    o=o*10+c-'0';c=getchar();}
		return o*f;
	}
}using namespace IO;
//#############以上是自定义技巧(可忽略)########## 
const int N=2e4+7,M=2e5+7,INF=0x3f3f3f3f,mod=1e8+7,P=131;
int h[N],e[M],ne[M],idx;
int dp[N];
int fa[N];
int n,m;
void add(int a,int b){
    
    
	e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
int find(int x){
    
    
	return x==fa[x]?x:fa[x]=find(fa[x]);
}
void dfs(int u,int fa){
    
    
	dp[u]+=dp[fa];
	for(int i=h[u];~i;i=ne[i]){
    
    
		int j=e[i];
		if(j==fa)continue;
		dfs(j,u);
	}
}
int main(){
    
    
	cin>>n>>m;
	mem1(h);
	for(int i=0;i<=n*2;i++)fa[i]=i;
	int op,a,b,root=n+1;
	for(int i=0;i<m;i++){
    
    
		cin>>op>>a>>b;
		if(op==1){
    
    
			a=find(a),b=find(b);
			if(a!=b){
    
    //判断是否在一个集合
				fa[a]=fa[b]=root;//构建一个新点,这个点为合并的集合根节点
				add(root,a);//建边
				add(root,b);
				root++;
			}
		}else{
    
    
			a=find(a);//查找当前节点的集合的根节点
			dp[a]+=b;//根节点+权值
		}
	}
	for(int i=n+1;i<root;i++){
    
    
		if(fa[i]==i)dfs(i,0);//树形dp,把根节点加的权值往下传
	}
	for(int i=1;i<=n;i++)cout<<dp[i]<<" ";
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43738764/article/details/109035131