2 条题解
-
1
這道題考察帶權並查集,帶權並查集與普通並查集的區別就是在每一條邊上都帶一個權值,然後我們在find的時候把這個點到其集合中的最初祖先的權值記錄下來,因為這個東西不會再改變了
那麼這道題,首先我們定義 表示我們 節點的父親, 表示 節點下麵有多少塊磚, 表示我們 這個點的集合中一共有多少塊磚
合併與詢問很簡單,讀者自己證明
#include<iostream> #include<cstdio> #define int long long using namespace std; bool Test_MLE_start; constexpr int N=3*1e5+10; int _=1,f[N],len[N],dis[N]; inline int reads(){ char c=getchar(); int sum=0,f=1; while(!isdigit(c)){ if(c=='-') f=-1; c=getchar(); } while(isdigit(c)){ sum=(sum<<3)+(sum<<1)+(c^'0'); c=getchar(); } return sum*f; } inline void files(){ freopen("A.in","r",stdin); freopen("std.out","w",stdout); } inline void clr(){ // Don't forget! } int finds(int x){ if(f[x]==x) return x; int fa=finds(f[x]); dis[x]+=dis[f[x]];return f[x]=fa; } void merges(int x,int y){ x=finds(x),y=finds(y); f[x]=y;dis[x]+=len[y];len[y]+=len[x]; } void asks(int x){finds(x);printf("%lld\n",dis[x]);} bool Test_MLE_end; signed main(){ // printf("%lf Mb\n",(&Test_MLE_end-&Test_MLE_start-1)/1024.0/1024.0); // files(); for(int i=1;i<=100000;i++) f[i]=i,len[i]=1; _=reads(); while(_--){ clr();int opt,x,y;opt=reads(); if(opt&1) x=reads(),y=reads(),merges(x,y); else x=reads(),asks(x); } return 0; } -
-1
带权并查集
每一摞砖作为一个集合
对于每个点 x(砖的编号),维护:
1、每块砖 x 所在集合的祖先 fa[x]
2、每块砖 x 所在的集合有多少块砖 siz[fa[x]]
3、x 到最上方砖块即祖先的距离dist[x]
带权并查集其实就是在查找、合并的时候顺便维护一些权值。
维护的信息根据题目可以自己设定。
思考:你还能想到维护哪些信息可以解决本题?
#include<bits/stdc++.h> using namespace std; const int N=30005; int fa[N], siz[N], dist[N]; int findfa(int x) { if(fa[x]==x)return x; int fx=findfa(fa[x]); dist[x]+=dist[fa[x]]; return fa[x]=fx; } int hebing(int x,int y) { int fx=findfa(x), fy=findfa(y); fa[fy]=fx; dist[fy]=siz[fx]; siz[fx]+=siz[fy]; } int main() { for(int i=1;i<N;++i) fa[i]=i, siz[i]=1, dist[i]=0; // 并查集初始化,维护三个值 int m; scanf("%d",&m); while(m--) { int op,x,y; scanf("%d",&op); if(op==1) { scanf("%d%d",&x,&y); hebing(x,y); } else { scanf("%d",&x); printf("%d\n",siz[findfa(x)]-dist[x]-1); } } return 0; }
- 1
信息
- ID
- 384
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 6
- 标签
- (无)
- 递交数
- 17
- 已通过
- 11
- 上传者