终于把清明节假期时自己挖的坑给补上了
题目来源:
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)的思维导图或者笔记(如果有笔记的话)