首页 > 其他分享 >Codeforces Round #747 (Div. 2) D // 扩展域并查集

Codeforces Round #747 (Div. 2) D // 扩展域并查集

时间:2022-10-15 00:44:29浏览次数:104  
标签:pa1 pb1 cntB cntA 747 查集 Codeforces pb2 find

题目来源:Codeforces Round #747 (Div. 2) D - The Number of Imposters

题目链接:Problem - D - Codeforces


题意

有\(n\)个人,每个人拥有\(imposter\)或\(crewmate\)的身份,身份为\(imposter\)的人总是说假话,身份为\(crewmate\)的人总是说真话。给定\(m\)句陈述,\(i\ j\ imposter/crewmate\),表示: \(i\) 说 \(j\) 的身份为 \(imposter/crewmate\)。

问:身份为\(imposter\)的人最多有多少个,若\(m\)个陈述存在矛盾,则输出\(-1\)。

数据范围:\(1 \le n \le 2·10^5\),\(\sum{n} \le 2·10^5\),\(1 \le m \le 5·10^5\),\(\sum{m} \le 5·10^5\).

思路:带权的扩展域并查集

我们将\(imposter\)简记为\(A\),\(crewmate\)简记为\(B\),对于每句陈述(\(i\ j\ A/B\)):

  • 当陈述为 \(i\ j\ A\) 时,有两种可能:若 \(i\) 为 \(A\),则 \(j\) 为 \(B\)(\(i\)说了谎);若 \(i\) 为 \(B\),则 \(j\) 为 \(A\)(\(i\)说真话)
  • 当陈述为 \(i\ j\ B\) 时,有两种可能:若 \(i\) 为 \(A\),则 \(j\) 为 \(A\)(\(i\)说了谎);若 \(i\) 为 \(B\),则 \(j\) 为 \(B\)(\(i\)说真话)

于是,我们可以用扩展域并查集,编号\([1,n]\)表示身份为\(A\),编号\([n+1,n+n]\)表示身份为\(B\),然后同时维护出两种可能的关系:

  • 当陈述为 \(i\ j\ A\) 时:合并 \(i\) 和 \(j+n\) ,合并 \(i+n\) 和 \(j\).
  • 当陈述为 \(i\ j\ B\) 时:合并 \(i\) 和 \(j\),合并 \(i+n\) 和 \(j+n\).

在维护每个连通块的同时,维护出连通块中\(A\)身份结点的数量\(cntA\),以及\(B\)身份结点的数量\(cntB\)。

由于每个人的身份只能是\(A\)或\(B\),不能又是\(A\)又是\(B\),那么出现矛盾的充要条件为:\(i\) 和 \(i+n\) 在同一连通块中,或 \(j\) 和 \(j+n\) 在同一连通块中。在每次合并后需判断一次当前是否存在矛盾,若已经存在矛盾,就不需要再进行合并了。

处理完所有陈述后,若不存在矛盾,则计算答案:遍历每个连通块,取\(cntA\)和\(cntB\)的较大值,累加到答案\(ans\)中。但由于我们同时考虑了两种可能情况,得到的答案其实是真正答案的\(2\)倍,因此最终答案应为\(\large \frac{ans}{2}\)。

时间复杂度:\(O(n + m\alpha(n))\).

代码

#include <bits/stdc++.h>
#define endl '\n'
using namespace std;

const int N = 400010;

int n, m, p[N], cntA[N], cntB[N];

int find(int x)
{
	return p[x] == x ? x : p[x] = find(p[x]);
}

void solve()
{
	cin >> n >> m;
	for(int i = 1; i <= n + n; i++) {
		p[i] = i;
		cntA[i] = i <= n, cntB[i] = i > n; 
	}

	bool flag = true;  // 陈述是否互不矛盾
	while(m--) {
		int a, b;
		string s;
		cin >> a >> b >> s;

		if(!flag) continue;

		int pa1 = find(a), pb1 = find(b);
		int pa2 = find(a + n), pb2 = find(b + n);
		if(s == "imposter") {
			if(pa1 != pb2) {
				cntA[pa1] += cntA[pb2], cntB[pa1] += cntB[pb2];
				p[pb2] = pa1;
			}
			// 判断合并后是否出现矛盾,已经出现矛盾就不再进行合并
			if(find(a) == find(a + n) || find(b) == find(b + n)) flag = false;
			if(!flag) continue;
			if(pa2 != pb1) {
				cntA[pa2] += cntA[pb1], cntB[pa2] += cntB[pb1];
				p[pb1] = pa2;
			}
		} else {
			if(pa1 != pb1) {
				cntA[pa1] += cntA[pb1], cntB[pa1] += cntB[pb1];
				p[pb1] = pa1;
			} 
			// 判断合并后是否出现矛盾,已经出现矛盾就不再进行合并
			if(find(a) == find(a + n) || find(b) == find(b + n)) flag = false;
			if(!flag) continue;
			if(pa2 != pb2) {
				cntA[pa2] += cntA[pb2], cntB[pa2] += cntB[pb2];
				p[pb2] = pa2;
			}
		}
		// 判断合并后是否出现矛盾,已经出现矛盾就不再进行合并
		if(find(a) == find(a + n) || find(b) == find(b + n)) flag = false;
	}

	if(!flag) {
		cout << -1 << endl;
		return;
	}

	int ans = 0;
	for(int i = 1; i <= n + n; i++) {
		if(p[i] == i) ans += max(cntA[i], cntB[i]);
	}
	cout << ans / 2 << endl;
}

int main()
{
	cin.tie(0);
	ios::sync_with_stdio(false);

	int test;
	cin >> test;
	while(test--) solve();

	return 0;
}

标签:pa1,pb1,cntB,cntA,747,查集,Codeforces,pb2,find
From: https://www.cnblogs.com/jakon/p/16793413.html

相关文章

  • Educational Codeforces Round 117 D
    D.X-MagicPair显然对于两个操作可以一眼识别是辗转相减可是我们怎么利用这个信息我们可以发现如果a>b;我们将更小的b替换成|a-b|那么我们显然又转回来了我们考虑......
  • Codeforces Round #827 (Div. 4) ABCDEFG
    https://codeforces.com/contest/1742A.Sum#include<bits/stdc++.h>usingnamespacestd;typedeflonglongLL;typedefpair<LL,LL>PII;constLLN=500200,M=2002;......
  • Codeforces Round #827 (Div. 4)(A~G)
     A.Sum1voidsolve(){2inta[3];3for(inti=0;i<3;i++)cin>>a[i];4sort(a,a+3);5if(a[2]==a[1]+a[0])cout<<"......
  • Codeforces Global Round 18 C
    C.Menorah显然对于每个操作我们是保留一个1所以我们当先是x个1的话做一次就是n+1-x个1并且我们只有这两种数量这样我们就可以特判无解了之后显然对于每两个操作我......
  • Codeforces Round #825 (Div. 2)(补题中)
    战绩:A.MakeAEqualtoB 实际上就只有两种操作可能,一种是遇到了不同的位置直接换,一种是换出0和1一样的个数后重新排列顺序,两种操作比较最小值输出。intmain(){......
  • Codeforces Round #827 (Div. 4) A - G
    A.Sumvoidsolve(){inta[3]={};cin>>a[0]>>a[1]>>a[2];sort(a,a+3);if(a[2]==a[0]+a[1])cout<<"YES\n";elsecout<<"NO......
  • Codeforces Round #780 D
    D.MaximumProductStrikesBack显然我们是不喜欢0的我们可以对0进行切割分成若干段然后我们要是是段数内乘积为负数显然我们也是不喜欢的我们必须要砍掉一个负数......
  • Codeforces Round #763 C
    C.BalancedStoneHeaps最小值最大显然二分考虑check首先我们从前往后做的话要考虑后面的消息显然不可取我们考虑从后往前做但是这里要注意的只有一点就是我们从......
  • Educational Codeforces Round 127 D
    D.InsertaProgression显然我们可以对a1——a2之间的数全部都插入期间显然是没有贡献的并且我们我们的1-x只用维护最小1和最大x即可显然要是我们要是mn中没有1......
  • Codeforces Round #787 F
    F.VladandUnfinishedBusiness和一般的求多个点都到达的最小距离不同这里规定了终点这样我们首先x-y这条链可以确定当然我们这条链可以通过让path[y]等于1因为树中......