智算之道第二场 情报站(并查集)

题目大意

现在我方已经查明,敌人通信所使用的加密方式依赖于一个长度为 nn 的数列,只要得知了这个数列中每个数的值,我方便可破解敌方的通信。通过深入敌人内部的内线人员的艰苦奋斗,我方逐渐获得了一些有用的情报,通过这些情报,整个数列正在被不断地破解。

先后有 m m 条情报被得知,每条情报是以下两种情况之一:

情况 1 1 :知道了数列中第 x x 个数的值

情况 2 2 :知道了数列中第 x x 个数和第 y y 个数的和

每得知一条情报,我方都试图破解数列中元素的值。作为情报部门核心技术人员的你,请编程实现如下功能:每次得知一条新情报,你都要计算当前已经能够确定出数列中的多少个数了。你比较笨,对于情况 2 这种情报,只能在已知其中一个数的情况下推出另一个数,不能通过若干情况 2 的情报列方程求解

输入格式

第一行,两个正整数 n , m n,m
接下来 m m 行,每行的第一个数是 t y p e type
如果 t y p e = 1 type=1 ,则接下来跟着一个整数 x x ,表示得知了数列中第 x x 个数的值;
如果 t y p e = 2 type=2 ,则接下来跟着两个空格隔开的整数 x , y x,y ,表示得知了第 x x 个数和 y y 个数的和

输出格式
输出 m m 行,每行包含一个非负整数,第 i i 行的非负整数表示在得知了前 i i 条情报之后数列中已经能够确定的数的数量

数据规模与约定

对于 100 % 100\% 的数据, 1 n , m 3 × 1 0 5 1 \le n,m \le 3\times 10^5

可能会有重复的情报,也可能出现 x = y x=y 的情况

样例输入

5 4
1 1
1 2
2 2 3
2 1 3

样例输出

1
2
3
3

分析

首先不难想到暴力的方法,也就是如果输入1每次 d f s dfs 找一个点和它相连的点,如果输入2就建立一条边。但是最后wa了,原因是,如果图中存在环,那么这一系列的点都会知道,但是 d f s dfs 是无法处理环的

然后就想到了并查集,那么我们用vector保存每个顶点为祖先相连的集合,如果输入操作1,那么该点祖先节点的集合全部可以得知,如果输入为2,先看这两个点是否均已知,如果有一个已知另外一个祖先的集合也可以全部得知;如果均未知且两个节点的祖先不同,那么合并为一个集合。最后注意因为操作二的两个数可以相等,那么相当于直接得知了这个数,特判一下即可

当然集合的合并要使用启发式合并

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=3e5+10;

vector<int> q[maxn];
bool vis[maxn];
int ans=0,f[maxn];

int Find(int x){
    return f[x]==x?x:f[x]=Find(f[x]);
}

void Union(vector<int> &x,vector<int> &y){
    if(x.size()<y.size()) swap(x,y);
    for(int i=0;i<y.size();i++)
        x.push_back(y[i]);
}

int main(){
    int n,m,x,y,op;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        f[i]=i;
        q[i].push_back(i);
    }
    while(m--){
        scanf("%d",&op);
        if(op==1){
            scanf("%d",&x);
            int fx=Find(x);
            for(int i=0;i<q[fx].size();i++) if(!vis[q[fx][i]]){
                vis[q[fx][i]]=1;
                ans++;
            }
            q[fx].clear();
        }else{
            scanf("%d%d",&x,&y);
            int fx=Find(x);
            int fy=Find(y);
            if(vis[fx] && vis[fy]){
                printf("%d\n",ans);
                continue;
            }
            if(x==y){
                for(int i=0;i<q[fx].size();i++) if(!vis[q[fx][i]]){
                    vis[q[fx][i]]=1;
                    ans++;
                }
                q[fx].clear();
                printf("%d\n",ans);
                continue;
            }
            if(vis[fx] && !vis[fy]){
                for(int i=0;i<q[fy].size();i++) if(!vis[q[fy][i]]){
                    vis[q[fy][i]]=1;
                    ans++;
                }
                q[fy].clear();
            }else if(!vis[fx] && vis[fy]){
                for(int i=0;i<q[fx].size();i++) if(!vis[q[fx][i]]){
                    vis[q[fx][i]]=1;
                    ans++;
                }
                q[fx].clear();
            }else{
                if(fx!=fy){
                    f[fy]=fx;
                    Union(q[fx],q[fy]);
                    q[fy].clear();
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}

后来从朋友那里明白,其实这个问题并不需要具体的集合元素,只需要知道集合中每个元素的个数,那么合并时只需合并数目,具体见代码:

#include <bits/stdc++.h>
using namespace std;
const int maxn=3e5+5;
int f[maxn];
bool vis[maxn];
int a[maxn];

int Find(int x){
    return f[x]==x?x:f[x]=Find(f[x]);
}

int main(){
    int n,m,op,x,y,ans=0;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        f[i]=i,a[i]=1;
    while(m--){
        scanf("%d",&op);
        if(op==1){
            scanf("%d",&x);
            int fx=Find(x);
            if(!vis[fx]){
                ans+=a[fx];
                vis[fx]=1;
            }
        }else{
            scanf("%d%d",&x,&y);
            int fx=Find(x),fy=Find(y);
            if(x==y || vis[fx] || vis[fy]){
                if(!vis[fx]){
                    ans+=a[fx];
                    vis[fx]=1;
                }
                if(!vis[fy]){
                    ans+=a[fy];
                    vis[fy]=1;
                }
            }
            if(fx!=fy){
                f[fy]=fx;
                a[fx]+=a[fy];
            }
        }
        printf("%d\n",ans);
    }

}

猜你喜欢

转载自blog.csdn.net/qq_44691917/article/details/107492476