D. The Number of Imposters

在这里插入图片描述
对于每个言论,可以确定一个关系,如果是x y im ,则说明x和y是同一类人,如果是x y cr ,则说明x和y不是一类人。可以把所有有联系的人放在一个图中,对图中的点进行染色,那么图中的点各自有着直接或间接地联系,那么每个点的颜色非黑即白。为了方便实现,若x , y是同一类人,那么x,y之间有一条权为1的边,若x,y不是同一类人,那么x,y之间有一条权为0的边。根据已有的边,就可以对点进行染色了。若染色过程中存在冲突,那就无解

code:

#include <bits/stdc++.h>

using namespace std ;

const int N = 2e5+10  ;

vector<pair<int,int>>v[N] ;

int t , n , m , flag , cnt0 , cnt1;
bool vis[N] ;
int color[N] ;

void dfs(int u){
    vis[u] = true ;
    if(color[u] == 0)cnt0++;
    else cnt1++;
    for(auto it : v[u]) {
        if(!vis[it.first]) {
            color[it.first] = color[u] ^ it.second ;
            dfs(it.first);
        }else{
            if((color[it.first] ^ color[u]) != it.second) flag = true;
        }
    }
}
void solve(){
    scanf("%d%d",&n,&m) ;
    flag = false ;
    for(int i = 0 ; i <= n ; i++) v[i].clear() , vis[i] = false  , color[i] = 0 ;
    for(int i = 0 ; i < m ; i++) {
        int x , y  ; char str[15] ;
        scanf("%d%d%s",&x,&y,str);
        if(str[0] == 'i') v[x].push_back({y,1}),v[y].push_back({x,1});
        else v[x].push_back({y,0}),v[y].push_back({x,0});
    }
    int ans = 0 ;
    for(int i = 1 ; i <= n ; i++) {
        if(!vis[i]){
            cnt0 = cnt1 = 0 ;
            dfs(i) ;
            ans += max(cnt0,cnt1);
        }
    }
    printf("%d\n",flag ? -1 : ans);
}

int main()
{
    //freopen("in","r",stdin);
    scanf("%d",&t) ;
    while (t--) solve() ;
    return 0 ;
}

补充并查集做法:


//这里采用乱序合并,会生成 1 ~ 2 * n某些编号为根的树(树根的分布是1 ~ 2*n) , 其中某棵树 i 的敌对点 i + n 不一定是树根 , 所以对于每棵树,要找根对应敌对点所在根。
//也可以采用有序合并,开始就不太理解有序合并,想了很久才明白,也就是让某点为根的同时,让其敌对点也为根,这样查找效率更高,优化了常数。
#include <bits/stdc++.h>

using namespace std ;
const int N = 2e5+10 ;

int p[N*2] , cnt[N*2] ;
int t , n , m;
int a[N*5] , b[N*5] ;
bool enemy[N*5] ;
char str[15] ;
int fr(int x){
    return x == p[x] ? p[x] : p[x] = fr(p[x]) ;
}
void merge(int x , int y)
{
    x = fr(x) , y = fr(y) ;
    if(x == y) return ;
    p[x] = y ;
    cnt[y] += cnt[x] , cnt[x] = 0;
}
int main()
{
//    freopen("in","r",stdin);
    scanf("%d",&t);
    while (t--) {
        scanf("%d%d",&n,&m);
        for(int i = 1 ; i <= n ; i++) p[i] = i , cnt[i] = 1 ;
        for(int i = n + 1 ; i <= 2 * n ; i++) p[i] = i ,cnt[i] = 0;
        for(int i = 1 ; i <= m ; i++){
            scanf("%d%d%s",&a[i],&b[i],str);
            if(str[0]=='i') enemy[i] = true ;
            else enemy[i] = false ;
        }
        bool flag = false ;
        for(int i = 1 ; i <= m ; i++) {
            int x = fr(a[i]) , y = fr(b[i]);
            int xe = fr(a[i] + n) , ye = fr(b[i] + n);
            if(enemy[i]){
                if(x == y || xe == ye) {
                    flag = true ;break;
                }
                merge(x , ye) ;
                merge(xe , y) ;
            }else{
                if(x == ye || y == xe) {
                    flag = true ;break;
                }
                merge(x  , y) ;
                merge(xe , ye);
            }
        }
        if(flag) {
            puts("-1") ;continue;
        }
        int Sum = 0 ;
        for(int i = 1 ; i <= n ; i++) {
            if(p[i] == i) {
                Sum += max(cnt[i],cnt[fr(i+n)]);
            }
        }
        for(int i = n + 1 ; i <= 2 * n ; i++){
            if(p[i] == i)  {
                Sum += max(cnt[i],cnt[fr(i-n)]);
            }
        }
        printf("%d\n",Sum / 2);
    }
    return 0 ;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值