【AcWing 239. 奇偶游戏】带权并查集

题目链接

题意:

有一段01序列,长度是n,现在给你m个询问,每个询问都有两个操作数和一个类型,x y even 表示 a[x]…a[y]有偶数个1,若是 x y odd 表示a[x]…a[y]有奇数个1,问至少到第几个询问和前面的询问是不符合的,,若全符合,则输出询问数。

分析:

假设咱们用s[x]表示01序列中前x个数的和,那么a[x]…a[y]有奇数个1表示s[y]和s[x-1]的奇偶情况是相反的,反之则相同,那么就把一段区间上的问题转化到了两个数上面,但是解决这个问题还需要一个性质,就是奇偶性的传递性,假如a 和 b奇偶性相同,b 和 c奇偶性相同,那么a 和 c奇偶性就相同,现在咱们就能想到用并查集这个数据结构了,d[x]表示x这个节点与这个集合的根节点的奇偶性相同不相同,若d[x]为0,则表示相同,为1则表示不同,下面请看代码吧:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define int long long
using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
typedef unsigned long long ull;
const int mod = 1e9+7;
const int N = 20010;
unordered_map<int,int> S;
int n,m;
int fa[N],d[N];
int get(int x){//离散化
	if(S.count(x) == 0){
		S[x] = ++n;
	}
	return S[x];
}
int find(int x){
	if(x != fa[x]){
		int t = find(fa[x]);
		d[x] ^= d[fa[x]];
		fa[x] = t;
	}
	return fa[x];
}
signed main(){
	cin>>n>>m;
	n = 0;
	for(int i=1;i<N;i++) fa[i] = i;
	int res = m;
	for(int i=1;i<=m;i++){
		int x,y;
		string op;
		cin>>x>>y>>op;
		x = get(x-1);y = get(y);
		int t = 0;
		if(op[0] == 'o') t = 1;
		int pa = find(x),pb = find(y);
		if(pa == pb){
			if((d[x] ^ d[y]) != t){
				res = i-1;
				break;
			}
		}
		else{
			fa[pa] = pb;
			d[pa] = d[x] ^ d[y] ^ t;//因为d[x] ^ d[y]需要等于t,在更新的时候d[x]^=d[pa],然后d[x]
									//就变成了d[y] ^ t,再^d[y]的话就等于t了,可以这么想
		}
	}
	cout<<res<<endl;
	return 0;
}

你可能感兴趣的:(学习日记,算法题目,并查集,算法,c++,图论)