终于把清明节假期时自己挖的坑给补上了
题目来源:
参考:
思路1:
思考:
- 题目看着,看到"发送信息后,又会发送到相邻的结点上面"这句话,觉得有点并查集的味道,但是后面又说:"计算出每个节点存储信息的大小",有些复杂!
- 按照本人理解,并查集是类似于树状数组的集合,但是没有权值,会不会有带权并查集呢?不妨利用数组 d d d来存放并查集中的每个元素所代表的结点权值。
- 依照题意,输入 1 1 1时,两个结点连接,也即"认大哥",输入 2 2 2时,发送消息
思路有了,先暴力试一下呗~
暴力代码:(7/10)
c++
#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代码:
c++
//树上差分+并查集 这就像一颗树
#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)的思维导图或者笔记(如果有笔记的话)