题目链接:Problem - 1594D - Codeforces
题目大意:
有n个玩家,两种身份,"imposter"总是说谎,"crewmate"总是说实话,现在给你m条信息,让判断最多有多少个"imposter"。如果给的消息矛盾,输出-1。
题目解析:
当c="imposter"的时候,如果i是诚实的,那么j就是说谎的
如果i是说谎的,那么j就是诚实的
即i、j总是相异。
当c="crewmate"的时候,如果i是诚实的,那么j也是诚实的
如果i是说谎的,那么j也是说谎的
即i、j总是相同。
带权并查集解法思路:
bool p=(c[0]=='i'),当c为"imposter"时,p为1;当c为"crewmate"时,p为0。
我们维护一个树,
如果p=1,那么当前节点 j 跟他的父节点 i 是相异的,我们维护它们之间的权值为1。
如果p=0,那么当前节点 j 跟他的父节点 i 是相同的,我们维护它们之间的权值为0。
通过递归维护,父节点到根节点的权值,可以找到当前节点到根节点的权值。
注:dis[j]*和dis[j]是一个东西,这里只是dis[j]*表示后一个状态。
如果当前ij已经连接,判断当前这句话是否正确,矛盾则输出-1;
否则,连接。
代码如下:
#include<iostream>
#include<algorithm>
#include<math.h>
#include<string.h>
#include<stdio.h>
#include<vector>
#include<map>
using namespace std;
//#define int long long
const int N=2e5+10;
int t,n,m;
int f[N],dis[N];
int cnt[N][2];
void init()
{
for(int i=1;i<=n;i++)
{
f[i]=i;
dis[i]=0;
cnt[i][0]=1;//跟i节点同类的数量
cnt[i][1]=0;//跟i节点异类的数量
}
}
int find(int x)
{
if(f[x]!=x)
{
int t=find(f[x]);
dis[x]^=dis[f[x]];//更新当前节点到根节点的距离
f[x]=t;
}
return f[x];
}
void solve()
{
cin>>n>>m;
init();
bool pd=0;
while(m--)
{
int i,j;
string s;
cin>>i>>j>>s;
bool p;
p=(s[0]=='i');
int a=find(i),b=find(j);
if(a==b)//连接了
{
if((dis[i]^dis[j])!=p)
{
pd=1;
}
}
else//没有连接的话 连接
{
f[a]=b;
dis[a]=p^dis[i]^dis[j];
cnt[b][0]+=cnt[a][dis[a]]; //如果a跟b是异类 就加上a的异类
cnt[b][1]+=cnt[a][1^dis[a]];
}
}
if(pd)
{
cout<<"-1\n";
return;
}
int sum=0;
for(int i=1;i<=n;i++)
{
if(find(i)==i)
{
sum+=max(cnt[i][0],cnt[i][1]);
}
}
cout<<sum<<endl;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>t;
while(t--)
{
solve();
}
return 0;
}
这里有一个被卡的点,就是我还没输入完就直接判"-1"了,导致后面的数据到下一组了,造成错误,多组输入要注意一下这个问题。
种类并查集解法思路:
有两种类型的人,一种诚实,一种说谎,我们开2*n的数组,每n个为一种类型。
如果 i j 同类 将merge(i,j) 和 merge(i+n,j+n)连接
如果 i j 异类 将merge(i,j+n) 和 merge(i+n,j)连接
用cnt数组计数,将前n个值赋为1,后n个赋为0。
代码实现:
#include<iostream>
#include<algorithm>
#include<string.h>
#include<math.h>
#include<stdio.h>
using namespace std;
#define int long long
const int N=2e5+10;
int f[N*2];
int n,m,t;
int cnt[N*2];
void init()
{
for(int i=1;i<=2*n;i++)cnt[i]=(i<=n),f[i]=i;
}
int find(int x)
{
if(f[x]!=x) return f[x]=find(f[x]);
return x;
}
void merge(int x,int y)
{
x=find(x),y=find(y);
if(x!=y)
{
cnt[x]+=cnt[y];
cnt[y]=0;
f[y]=x;
}
}
void solve()
{
cin>>n>>m;
init();
bool flag=0;
while(m--)
{
int i,j;
string s;
cin>>i>>j>>s;
bool c=(s[0]=='i');
if(c)//异
{
if(find(i)==find(j))
{
flag=1;
continue;
}
else
{
merge(i,j+n);
merge(i+n,j);
}
}
else//同
{
if(find(i)==find(j+n))
{
flag=1;
continue;
}
else
{
merge(i,j);
merge(i+n,j+n);
}
}
}
if(flag)
{
cout<<"-1"<<endl;
return ;
}
int ans=0;
for(int i=1;i<=n;i++)
{
ans+=max(cnt[i],cnt[i+n]);
}
cout<<ans<<endl;
}
signed main()
{
cin>>t;
while(t--)
{
solve();
}
return 0;
}
还有一种判断矛盾的方法,就是看find(i)==find(i+n),如果自己跟自己连接,显然矛盾直接输出-1。
代码实现:
#include<iostream>
#include<algorithm>
#include<math.h>
#include<string.h>
#include<stdio.h>
using namespace std;
const int N=2e5+10;
int f[N*2],cnt[N*2];
int t,n,m;
void init()
{
for(int i=1;i<=2*n;i++)
{
f[i]=i;
cnt[i]=(i<=n);
}
}
int find(int x)
{
if(f[x]!=x)return f[x]=find(f[x]);
return f[x];
}
void merge(int x,int y)
{
x=find(x),y=find(y);
if(x!=y)
{
cnt[x]+=cnt[y];
cnt[y]=0;
f[y]=x;
}
}
void solve()
{
cin>>n>>m;
init();
while(m--)
{
int i,j;
string s;
cin>>i>>j>>s;
bool c=(s[0]=='i');
if(c)//异类
{
merge(i+n,j);
merge(i,j+n);
}
else
{
merge(i,j);
merge(i+n,j+n);
}
}
int sum=0;
for(int i=1;i<=n;i++)
{
int x=find(i),y=find(i+n);
if(x==y)
{
cout<<"-1"<<endl;
return ;
}
sum+=max(cnt[i],cnt[i+n]);
}
cout<<sum<<endl;
}
signed main()
{
cin>>t;
while(t--)
{
solve();
}
return 0;
}
END---