Codeforces Round #747 (Div. 2) D. The Number of Imposters

传送门:The Number of Imposters

大意:n 个人 ,每个人要么是好人,要么是坏人,好人只说真话,坏人只说假话。给定 一些关系,即a b c 代表 a说b的身份是 c。现要确定他们的什么,在不矛盾的前提下,问最多可以有多少坏人。输出坏人的人数,若关系存在矛盾输出-1。

思路:a 说 b 的身份是 c ,我们坏人记作c=0,好人记作c=1。当 c=0 时,即 a说b 是坏人,当 a是好人是,那么 b 就是坏人,当a 是坏人时,b实际上是好人。二人身份相异。

当 c=1 时,a说 b 是好人,当 a是好人时,那么b也是好人,当 a是坏人时,b也是坏人,二人身份相同。

通过分析我们发现,二人的身份要么相同,要么相异。并且具有传递性,即 ab 相异,bc 相异,那么 ac相同。所以我们可以用带权并查集来写,利用到根节点的距离奇偶性来区别身份异同,即到根节点的距离为偶数时与根节点身份相同,为奇数时与根节点身份不同。

当两人已经在用同一个连通块中,并且之前的身份信息和新输入的身份信息不一样时产生矛盾。

代码如下:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=200010;
int fa[N],dt[N],cnt[N][2];
int n,m;
void init()
{
	for(int i=1;i<=n;i++)
	{
		fa[i]=i;
		dt[i]=0;
		cnt[i][0]=1;
		cnt[i][1]=0;
	}
}
int find(int x)
{
	if(fa[x]!=x)
	{
		int px=find(fa[x]);
		dt[x]^=dt[fa[x]];
		fa[x]=px;
	}
	return fa[x];
}
void solve()
{
	cin>>n>>m;
	init();
	bool f=1;
	for(int i=1;i<=m;i++)
	{
		char str[10];
		int a,b,c;
		cin>>a>>b>>str;
		c=(str[0]=='i'); // 这样赋值可以做到下面不用分类讨论
		int pa=find(a),pb=find(b);
		if(pa==pb)
		{
			if((dt[a]^dt[b])!=c)f=0;
		}
		else 
		{
			fa[pb]=pa;
			dt[pb]=c^dt[a]^dt[b];//经过推导推出公式,省的分类讨论
			cnt[pa][0]+=cnt[pb][dt[pb]];
			cnt[pa][1]+=cnt[pb][dt[pb]^1];
		}
	}
	if(!f)
	{
		cout<<-1<<"\n";
		return ;
	}
	int res=0;
	for(int i=1;i<=n;i++)
	{
		int pi=find(i);
		if(pi==i)res+=max(cnt[pi][0],cnt[pi][1]);
	}
	cout<<res<<"\n";
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
   	int _=1;
   	cin>>_;
   	while(_--)solve();
   	return 0;
}

总结:各种关系约束、限制类的匹配问题,看能不能用并查集写。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值