首页 > 其他分享 >并查集的具体应用 CF1213G CF444E [HNOI2005]狡猾的商人

并查集的具体应用 CF1213G CF444E [HNOI2005]狡猾的商人

时间:2023-06-22 22:33:34浏览次数:46  
标签:cha val int 查集 CF1213G fa HNOI2005 find

每当我们看到“最大值最小”“路径上的最大最小值”等字眼时,我们就可以考虑并查集。

我们可以尝试把这些问题转化为某种意义上按单调顺序的合并,利用并查集求解答案。以下时两例并查集的巧妙应用。

CF1213G Path Queries

注意“最大权值不大于q”,加上允许离线,我们可以把边按照权值排序,并一条一条加边,即合并两端点所在的连通块。设当前边端点为 u,v,权值为w,因为排过序,所以一条边的端点所在联通块内的边权一定比w小,也就是说,最大权值等于q的简单路径就会有siz【u】*siz【v】条,也就是当前边对答案的贡献。最后用前缀和合并一下,就能得到答案。这道题做法很多,点分治,Kruskal 重构树等均可,但我认为并查集是最好写的。

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=5e5+55;
int siz[N],fa[N],m,n;
struct edge{
    int u,v,w;
}e[N<<1];
int q[N],ans[N];
int find(int x){
    if(x==fa[x]) return x;
    return fa[x]=find(fa[x]);
}
bool cmp(edge a,edge b){
    return a.w<b.w;
}
signed main(){
    cin>>n>>m;
    for(int i=1;i<n;i++){
        cin>>e[i].u>>e[i].v>>e[i].w;
    }
    for(int i=1;i<=n;i++) fa[i]=i,siz[i]=1;
    sort(e+1,e+n,cmp);
    for(int i=1;i<=m;i++) cin>>q[i];
    //sort(q+1,q+1+m);
    for(int i=1;i<n;i++){
        //ans[e[i].w]=ans[e[i-1].w];
        int u=e[i].u,v=e[i].v;
        int x=find(u),y=find(v);
        ans[e[i].w]+=siz[x]*siz[y];
        fa[y]=x;siz[x]+=siz[y];    
    }
    for(int i=1;i<N;i++) ans[i]+=ans[i-1];
    for(int i=1;i<=m;i++){
        cout<<ans[q[i]]<<" ";
    }
}
View Code

 

CF444E DZY Loves Planting

此题正解是二分答案+网络流,并且出现在我校网络流专题考试中。然而,除了CF官方题解,似乎没有哪个大冤种拿网络流写(除了考场上的我)

注意到题干中的min,求最大值的最小值最大,且离线,和上一题套路一样,把边按照边权从小到大排序,一个一个加。则连通块内边权一定小于当前边,即两个连通块之间的路径最大值就是这个边的权值,我们只需判断是否合法即可。对于当前已经合并的点,我们都需要给他配一个未合并的点,这样 g(x,y)才会 大于等于w,如果都能分配到,则该答案合法。

形式化的,设siz[u]表示u的连通块的大小,sum为所有x[i]之和,val[u]表示u的连通块内x[i]之和,那么,如果 $size[u]\le sum-val[u]$ ,则该答案合法。我们甚至完全不用二分,只需要按边权枚举每一条边即可,在加边,也就是合并区间的时候,更新fa,siz,val的值。

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=2e4+6;
int n,m,fa[N],siz[N],val[N],sum;
//val 表示 该连通块中xi的和 
int find(int u){
    if(fa[u]==u) return u;
    return fa[u]=find(fa[u]);
}
struct edge{
    int u,v,w;
}e[N<<1];
bool cmp(edge a,edge b){
    return a.w<b.w;
}
signed main(){
    cin>>n;
    for(int i=1;i<n;i++){
        cin>>e[i].u>>e[i].v>>e[i].w;
    }
    sort(e+1,e+1+n-1,cmp);
    for(int i=1;i<=n;i++){
        fa[i]=i;
        siz[i]=1;
    }
    for(int i=1;i<=n;i++){
        cin>>val[i];
        sum+=val[i];
    }
    for(int i=1;i<=n-1;i++){
        int u=e[i].u,v=e[i].v;
        int x=find(u),y=find(v);
        fa[x]=y;
        siz[y]+=siz[x];val[y]+=val[x];
        if(siz[y]>sum-val[y]){
            cout<<e[i].w;
            return 0;
        }
    }
    cout<<e[n-1].w;
    return 0;
}
View Code

 

[HNOI2005]狡猾的商人

此题的标准做法是差分约束,但是用带权并查集也可以。我觉得他们本质是相同的,而显然带权并查集更好写。

对于此题而言,每条信息(l,r,w),将l,r放入一个集合中,并维护c[l]=s[fa]-s[l],c[r]=s[fa]-s[r],若fa[l]==fa[r],则判断 c[l]-c[r] 与 w 是否相等即可。(这里要用路径压缩,fa也就是该集合的根)

#include<bits/stdc++.h>
using namespace std;
int fa[666],cha[666],T,n,m,x,y,z,p;
int s[666];
int find(int x){//带权并查集 
    if(x==fa[x]) return fa[x];
    else{
        int t=find(fa[x]);
        cha[x]+=cha[fa[x]];
        fa[x]=t;
        return fa[x];
    }
}
int main(){
    cin>>T;
    while(T--){
        p=1;
        cin>>n>>m;
        for(int i=0;i<=n;i++){fa[i]=i;cha[i]=0;}
        for(int i=1;i<=m;i++){
            cin>>x>>y>>z;
            x--;//qian zhui he
            if(find(x)!=find(y)){
                cha[fa[y]]=cha[x]-cha[y]-z;
                fa[fa[y]]=fa[x];
            }else{
                if(cha[x]-cha[y]!=z) p=0;
            }
        }
        if(p) cout<<"true"<<endl;
        else cout<<"false"<<endl;
    }
    return 0;
}
View Code

 

标签:cha,val,int,查集,CF1213G,fa,HNOI2005,find
From: https://www.cnblogs.com/DongPD/p/17498485.html

相关文章

  • abc049d <并查集>
    https://atcoder.jp/contests/abc049/tasks/arc065_b//https://atcoder.jp/contests/abc049/tasks/arc065_b//使用两个并查集维护连通关系//求并集,使用每个并查集的祖宗节点组成的pair表示这个交集#include<iostream>#include<algorithm>#include<map>usingnames......
  • 并查集
    并查集模板: #include<bits/stdc++.h>#defineMaxsize100005//只需要改这里就可以usingnamespacestd;intfa[Maxsize],rankk[Maxsize];inlinevoidinit(intn)//初始化{for(inti=1;i<=n;++i){fa[i]=i;rankk[i]=1;}}intfind(intx)/......
  • HDU 3277 Marriage Match III(并查集+二分+最大流)
    题意:和HDU3081一样的题意,只不过多了一个条件,每个女孩除了能选自己喜欢的男生之外,还能选不超过K个自己不喜欢的男生,问游戏最多能进行几轮思路:除了选喜欢的,还能选任意K个不喜欢的,怎么建图呢?一开始我想每个女孩连喜欢的男孩,而且选K个不喜欢的男孩也连边,可是这K个要怎么确定呢?这种显然......
  • HDU 3081 Marriage Match II(二分+并查集+最大流)
    题意:有N个女孩要与N个男孩玩配对游戏.每个女孩有一个可选男孩的集合(即该女孩可以选自己集合中的任意一个男孩作为该轮的搭档).然后从第一轮开始,每个女孩都要和一个不同的男孩配对.如果第一轮N个女孩都配对成功,那么就开始第二轮配对,女孩依然从自己的备选男孩集合中选择,但是不能......
  • HDU - 2473 (并查集+设立虚父节点(马甲))
    涉及到并查集的删除操作,比较复杂,可以利用虚设父节点的方法:例如:有n个节点,进行m次操作.首先将0~n-1的节点的父节点设置为i+n,n~2n+1的节点的父节点设置为本身,有m次操作,所以要用到m个虚节点,例如0,1,2,3,4,5的父节点为7,8,9,10,11,把他们插入2的集合,所以他们的根节点都为8,当把2从集合......
  • 数据结构与算法分析(Java语言描述)(24)—— 并查集的路径压缩
    packagecom.dataStructure.union_find;//我们的第五版Union-FindpublicclassUnionFind5{//rank[i]表示以i为根的集合所表示的树的层数//在后续的代码中,我们并不会维护rank的语意,也就是rank的值在路径压缩的过程中,有可能不在是树的层数值//这也是......
  • POJ2236(并查集)
    题目:http://poj.org/problem?id=2236 题意:给定n个点的坐标,然后选出其中的一些点出来,问在这些点中的指定的两点是否连通。#include<iostream>#include<string.h>#include<stdio.h>usingnamespacestd;constintN=1005;structPoint{intx,y;};Pointp[N];i......
  • UVALive - 6889[并查集+STL]
    题目链接:https://vjudge.net/contest/301219#problem/F 解题思路:枚举每个矩形的时候,看它是否需要和其他人合并只需要查看它的外形边框是否又被标记,这个可以直接用离散化,然后set存一下每个矩形四个格子,就可以用log(n)找到合并的矩形,然后后并查集并一下就好了。#include<bits/std......
  • hdu 3038(种类并查集)
    题目大意:有n次询问,给出a到b区间的总和,问这n次给出的总和中有几次是和前面已近给出的是矛盾的解题思路:这道题第一次接触很难往并查集方向去思考。这里使用的并查集很灵活,不仅仅要记录其父亲节点,同时还要记录该节点到父亲节点的总和。在更新时,[l,r]要变成[l-1,r],比如有区间[1,2],[3,4......
  • poj 2985(并查集+线段树求K大数)
    解题思路:这道题并查集很容易,合并时找到父节点就直接加上去就ok了。关键是如何求K大数,我一直在想用线段树怎么写,一开始想如果直接记录数的大小那肯定是没戏了,借鉴了一下别人的思路:区间[a,b]记录的是所有的数里面,等于a,a+1,a+2,......,b-1,b的个数。看到这里就应该明白了,这里线段树的用法......