南华大学 复读机(并查集)

链接:https://ac.nowcoder.com/acm/contest/699/B?&headNav=acm&headNav=acm
来源:牛客网

题目描述

        在某华大学里有一个无聊的群组,群内的所有成员都是复读机,他们疯狂复读着别人的消息。然而复读机们在群内也是有阵营的,一个阵营的复读机会根据心情选择是否复读同一个阵营的成员的消息,但绝对不会去复读其他阵营的消息。

        群内有n个人(标号1-n号)。现在群内聊天记录中一共有m条消息。请根据这些消息,判断出如果群内第i号成员(1<=i<=n)发一条十分有趣的消息,在同一阵营的成员都会去复读这条消息情况下,那么这条消息会被复读几次?

如果无法判断i号成员和j号成员是否为同一阵营,则视为不同阵营。

输入描述:

输入包括m+1行。第一行包括两个整数n,m;其后的m行为当前的聊天记录,每行包括一个数字i和一个字符串s(表示i发了一条消息)。(保证1<=i<=n,m<=100000,消息的字符串总长不超过100000)

输出描述:

输出一行,包括n个数字。第i个数字表示第i号成员的有趣消息会被复读几次(1<=i<=n)。数字与数字之间用一个空格分隔。
示例1

输入

复制
3 5
1 a
2 a
1 b
2 c
3 c

输出

复制
2 2 2

说明

对于样例1: 由聊天记录的前两行可以得出2号和1号为同一阵营,由最后两行可得3号和2号为同一阵营,所以三者为同一阵营,他们三个发送的消息会被复读2次,故输出为2 2 2;
示例2

输入

复制
3 3
1 a
2 b
3 c

输出

复制
0 0 0

说明

对于样例2:1,2,3号无法判断是否为同一阵营,视为不同阵营。

备注:

对于复读的概念:

若聊天记录为

1 a

2 a

3 a

属于2,3号复读了1号的消息。

若聊天记录为

1 a

2 b

3 a

不构成任何复读,1的‘a‘和3的‘a’均属于自己原创消息。

思路:在复读机的圈子内,,其他成员会复读该圈子内某一成员的内容,所以我们要用并查集将同一个圈子内的人联系在一起,然后再统计一个圈子内有多少个人,(同祖先的为一个圈子),记录一个圈子有多少人,只需要开一个数组,然后都记录在祖先身上就可以啦!
#include<iostream>
#include<string>
#include<vector>
#include<cstdio>
#include<map>
using namespace std;
const int N=100000+7;
int pre[N];
int sum[N];
int find(int x){//路径压缩后pre[i]=j则j就是i的树根节点
    int r=x;
    while(r!=pre[r]){
        r=pre[r];
    }
    int k=x,j;
    while(k!=r){
        j=pre[k];
        pre[k]=r;
        k=j;
    }
    return r;
}
void join(int x,int y){
    int fx=find(x),fy=find(y);
    if(fx!=fy){
        pre[fy]=fx;
    }
}
int main()
{
    ios::sync_with_stdio(false);
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        pre[i]=i;
    int x1,x;
    string y1,y;
    for(int i=1;i<=m;i++){
        if(i==1)
            cin>>x1>>y1;
        else {
            cin>>x>>y;
            if(y==y1){
                join(x1,x);
            }
            x1=x,y1=y;
        }
    }
    for(int i=1;i<=n;i++){
        sum[find(i)]++;//数据的记录
    }
    for(int i=1;i<=n;i++){
        printf("%d ",sum[find(i)]-1);//别忘了减去自己
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Accepting/p/11311024.html
今日推荐