Atcoder D - Friend Suggestions(并查集)

题意:
n n n个人, m m m对双向的朋友关系,还有 k k k对破裂的关系。
现在让你找 i i i的候选朋友,候选朋友是 i i i的朋友的朋友(间接朋友关系),并且还不是破裂关系。问每个 i i i有多少个候选朋友。
思路:
蠢死了。。。。
朋友的传递关系显然可以用并查集。我们进行 d f s dfs dfs并进行合并。那么对于任意点 i i i来说,它所在并查集的连通块中都是它可以通过它的朋友可以到达的,但是这其中也有和它直接是朋友的和有破解关系的朋友。我们用这个连通块的大小 - 和它直接是朋友的 - 有破裂关系的 - 他自己 = ans。

struct Edge{
    int next;
    int to;
}edge[N];
int head[N],tot;
inline void add(int from,int to){
    edge[++tot].next = head[from];
    edge[tot].to = to;
    head[from] = tot;
}
int cnt[N];
int pre[N];
int Find(int x) {return pre[x]==0?x:pre[x] = Find(pre[x]);}
int Size[N];
int ans[N];
void join(int x,int y){
    int q = Find(x),p = Find(y);
    if(q != p){
        if(Size[q] > Size[p]) pre[p] = q,Size[q] += Size[p];
        else pre[q] = p,Size[p] += Size[q];
    }
}
bool vis[N];
void dfs(int x){
    vis[x] = 1;
    for(int i = head[x];i;i = edge[i].next){
        int  y = edge[i].to;
        if(!vis[y]){
            join(x,y);
            dfs(y);
        }
    }
}
int main(){
    int n = read(),m = read(),k = read();
    fill(Size+1,Size + n +1,1);
    rep(i,1,m){
        int u = read(),v = read();
        cnt[u] ++;//储存直接边
        cnt[v] ++;
        add(u,v);
        add(v,u);
    }
    for(int i = 1;i <= n;++i) dfs(i);//进行集合关系的划分
    rep(i,1,k){
        int u = read(),v = read();
        if(Find(u) == Find(v)) cnt[u] ++,cnt[v] ++;//如果这两个破裂关系在一个连通块,那么就减去
    }
    rep(i,1,n) cout << Size[Find(i)] - cnt[i] - 1<<' ';
}

你可能感兴趣的:(图论算法—并查集)