并查集题集

hdu1232:http://acm.hdu.edu.cn/showproblem.php?pid=1232

代码:

#include
#define MAXN 100005
int f[MAXN];

void init()
{
    for(int i=0;i

CodeForces - 977E:https://vjudge.net/problem/CodeForces-977E

题意:

给你一个图,问你有几个没有杂边的单环(度全为2)

解析:

度为1的点不用管,连续边,判断是否连通,如果连通,ans++,否则连接这个边

ac:

#include
#define MAXN 400005
using namespace std;
int f[MAXN];
int in[MAXN];
int ans=0;
void init()
{
    for(int i=0;i

hdu1272:https://vjudge.net/problem/HDU-1272

题意:判断是否连通且无环

代码:

#include
#define MAXN 1000005
using namespace std;
int vis[MAXN];
int f[MAXN],sign;
int n,m,k,maxs;

void init()
{
    maxs=sign=k=0;
    memset(vis,0,sizeof(vis));
    for(int i=1;i1)
                sign=1;
            if(!sign)
                printf("Yes\n");
            else
                printf("No\n");
            init();
        }
        else{
            maxs=max(maxs,max(n,m));
            unite(n,m);
            vis[n]=vis[m]=1;   ///标记
        }
    }
    return 0;
}

https://pintia.cn/problem-sets/994805046380707840/problems/994805063963230208

题意:

按顺序失去城市,判断对图的连通性的改变

解析:

如果破坏连通性,首先呢个城市集合独立,还会造成另外的集合分开

如果只是不同集合数+1,呢是有个点与它原来集合分开

如果不改变集合数,呢是呢个点是孤立点

暴力模拟所以情况

ac:

#include
#define MAXN 10005
using namespace std;
int a[MAXN],b[MAXN];
int f[MAXN];
int vis[MAXN];

void init()
{
    for(int i=0;i

https://ac.nowcoder.com/acm/contest/1080/B

题意:

用并查集模拟链表

#include
#define MAXN 2000005
using namespace std;
int a[MAXN];
int f[MAXN];
int c[MAXN];
 
void init(int n)
{
    for(int i=0;i<=n;i++)
        f[i]=i;
}
 
int get(int x)
{
    if(x!=f[x])
        f[x]=get(f[x]);
    return f[x];
}
 
void unite(int a,int b)
{
    f[get(a)]=get(b);
}
 
int main()
{
    int n;
    scanf("%d",&n);
    init(n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    for(int i=1;i<=n;i++)
    {
        int x=a[i]%n;
        if(c[x]==0)
            c[x]=i;
        else{
            while(c[x]!=0)
            {
                if(x+1

或者用set也可以做:

#include 
using namespace std;
typedef long long ll;
const ll MOD=998244353;
const int maxn=1e6+10;
int ma[maxn];
sets;
int main()
{
    s.clear();
    int n;
    scanf("%d",&n);
    for(int i=0;i::iterator it;
        it=s.lower_bound(x);
        if(it!=s.end()){
            k=*it;
            s.erase(it);
        }
        else{
            it=s.begin();
            k=*it;
            s.erase(it);
        }
        ma[k]=ans;
    }
    for(int i=0;i

http://codeforces.com/contest/1139/problem/C

题意:

给定n-1条边一棵树(无环图),一个k,一些边是红边,一些是黑边

问有多少个种路径(路径上有k个点)是会过红边的?

解析:

总共有n^k种情况,减到只过黑边的就是答案

用并查集判断连通,找每个连通快内的连通点数目x

ans=n^k-(x(1)^k+x(2)^k+...x(n)^k)

含n个元素的集合选取k个元素有n^k种

ac:

#include
#define ll long long
#define mod 1000000007
#define MAXN 2000005
using namespace std;
vector vc[MAXN];
int f[MAXN];
int bb[MAXN];
int vis[MAXN];
 
ll qpow(ll a,ll b)
{
    ll ans=1;
    while(b)
    {
        if(b&1) ans=(ans*a)%mod;
        a=(a*a)%mod;
        b>>=1;
    }
    return ans;
}
 
void init()
{
    for(int i=0;i

http://codeforces.com/contest/1213/problem/G

题意:

给定一棵树,树边有边权,然后按顺序删除删除一些边,问你这棵树上不含这些边的二元组个数

解析:

离线按边权从小到大加入边,然后判断连通块的个数

用并查集判连通,每个连通块的贡献是:sz*(sz-1)/2.

解析:

#include
#define MAXN 200005
#define ll long long
using namespace std;
struct node
{
    ll u,v,w;
    friend bool operator <(node a,node b)
    {
        return a.wval[y])
        swap(x,y);
    val[y]+=val[x];
    f[x]=y;
}
ll ans[MAXN]={0};
 
int main()
{
    init();
    ll n,m,x;
    scanf("%lld%lld",&n,&m);
    for(ll i=1;i<=n-1;i++)
        scanf("%lld%lld%lld",&ee[i].u,&ee[i].v,&ee[i].w);
    sort(ee+1,ee+n-1+1);
    ll st=0,ed=0;
    for(ll i=1;i<=n-1;)
    {
        ll k=ee[i].w;
        while(i<=n-1&&ee[i].w==k)
        {
            unite(ee[i].u,ee[i].v);
            i++;
        }
        for(ll i=st;i

并查集还用在缩点中,根据题目意思把相同性质的元素,归属同一集合,将他们视作同一类东西

D. Gourmet choice

链接:D - Gourmet choice

题意:

给两个数组,长度分别为n,m.给两个数组的关系

构造出最小的两个数组,如果无法构造,输出NO,否则YES,输出两个数组

解析:

并查集(缩点)+拓扑排序

首先要判断是否有环,有环NO

对与"=",我们把它合成一个点,用并查集

用拓扑排序判断是否有环和构造数组

ac:
 

#include
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
#define IO ios_base::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
#define ll long long
#define MAXN 5005
using namespace std;
 
char str[1010][1010]={0};
int f[MAXN];
int in[MAXN]={0};
vector mp[MAXN];
int reward[MAXN]={0};
int n,m;
 
void init(){
    rep(i,1,MAXN)
        f[i]=i;
}
int find(int x){
    return x==f[x]?x:f[x]=find(f[x]);
}
void unite(ll a,ll b){
    ll x=find(a),y=find(b);f[x]=y;
}
 
void topo()
{
    queue que;
    for(int i=1;i<=n+m;i++)
        if(in[i]==0)
        {
            reward[i]=1;
            que.push(i);
        }
    int count=0;
    while(!que.empty())
    {
        int top=que.front();
        que.pop();
        count++;
        for(int i=0;i')
            {
                mp[find(n+j)].push_back(find(i));
                in[find(i)]++;
            }
            else {
                mp[find(i)].push_back(find(n+j));
                in[find(n+j)]++;
            }
        }
    topo();
    return 0;
}

题目链接:http://lx.lanqiao.org/problem.page?gpid=T114

问题描述
  C国由n个小岛组成,为了方便小岛之间联络,C国在小岛间建立了m座大桥,每座大桥连接两座小岛。两个小岛间可能存在多座桥连接。然而,由于海水冲刷,有一些大桥面临着不能使用的危险。

  如果两个小岛间的所有大桥都不能使用,则这两座小岛就不能直接到达了。然而,只要这两座小岛的居民能通过其他的桥或者其他的小岛互相到达,他们就会安然无事。但是,如果前一天两个小岛之间还有方法可以到达,后一天却不能到达了,居民们就会一起抗议。

  现在C国的国王已经知道了每座桥能使用的天数,超过这个天数就不能使用了。现在他想知道居民们会有多少天进行抗议。
输入格式:
  输入的第一行包含两个整数n, m,分别表示小岛的个数和桥的数量。
  接下来m行,每行三个整数a, b, t,分别表示该座桥连接a号和b号两个小岛,能使用t天。小岛的编号从1开始递增。
输出格式:
  输出一个整数,表示居民们会抗议的天数。

解析:

前一天可以到达,后一天无法到达,判连通

我们逆着建图,时间从大到小排序

合并,如果不连通sum++(且这天没有被标记),并且标记这一天

ac:

#include
#define MAXN 100005
using namespace std;
int n,m;
int a,b,t,sum;
 
struct node
{
    int a,b,t;
}q[MAXN];
int f[MAXN];
int vis[MAXN];
 
int cmp(node x,node y)
{
    return x.t>y.t;
}
 
int find(int x)
{
    if(f[x] != x)
       f[x]=find1(f[x]);
    return f[x];
}
 
int unite(int x,int y)
{
    int fx = find1(x),fy = find1(y);
    if(fx != fy)
    {
        f[fx] = fy;
        return 0;
    }
    return 1;
}
 
int main()
{
    scanf("%d%d",&n,&m);
    sum=0;
    for(int i=1;i<=MAXN;i++)
    {
        f[i]=i;
        vis[i]=0;
    }
    for(int i=1;i<=m;i++)
        scanf("%d%d%d",&q[i].a,&q[i].b,&q[i].t);
    sort(q+1,q+m+1,cmp);
    for(int i=1;i<=m;i++)
    {
        int w=mix(q[i].a,q[i].b);
        if(w==0&&vis[q[i].t]==0)
        {
            sum++;
            vis[q[i].t]=1;
        }
    }
    printf("%d\n",sum);
    return 0;
}

题目地址:https://nanti.jisuanke.com/t/41384n

题意:

给定一个n,q次操作,每次操作有两种情况:(1,x)或者(2,x)(1<=x<=n<=1e9)

1   x,将x删除

2   x,输出大于等于x,并且存在的数

解析:

离散化的并查集

开始用unordered_map超时了,查询太多了,unordered_map也不是万能的

ac:

#include
#define MAXN 2000005
#define ll long long
using namespace std;
int b[MAXN];
int f[MAXN];

int get(int x)
{
    if(f[x]!=x)
        f[x]=get(f[x]);
    return f[x];
}
void init()
{
    for(int i=0;i

 

你可能感兴趣的:(并查集)