描述
题解
这个问题我想半天也没想到怎么和并查集挂上钩了,看来是我并查集太弱了,找了找题解才搞懂了,但是感觉网上的题解前篇一律,开头讲的很容易懂,但是后边到为什么要开两倍大小的数组讲的却不是那么容易理解,所以我就按照我自己的理解再写一下吧,权当补充吧,如果我的理解错了,烦请众神拍砖~~~
首先,我们知道如果 [left, right] 为even
,那么说明这里有偶数个1,所以可以判定,[1, left - 1] 和 [1, right] 的奇偶性相同,可以划分到一个集合内;
其次,如果 [left, right] 为odd
,那么说明这里有奇数个1,所以可以判定,[1, left - 1] 和 [1, right] 的奇偶性不同,同样可以放到一个集合内;
但是问题来了,如果不做任何处理就同样放在一个集合,那么肯定是毫无意义的,我们需要第一种情况的集合为相同奇偶性的集合,第二种情况的放在不同奇偶性的集合,那么问题来了,我们如何搞这个集合呢?
网上很多网友都是说将数组开大两倍,用 [n + 1, 2 * n] 来表示不同的集合([1, n] 自然就表示相同了),实际上这样解释并不合适,根据我对代码的理解,我感觉可以这样定义,同样是开两倍大的数组,不过 [1, n] 用来表示该区间(前缀区间,希望我妄下定义大家可以理解我的意思)为奇(偶),然后偏移 n 对应表示区间为偶(奇)。
所以,第一种情况有分为两种,
都是奇(偶),join(left - 1, right);
,
都是偶(奇),join(left - 1 + N, right + N);
;
同理,第二种情况也可以分为两种,
前奇(偶)后偶(奇),join(left - 1, right + N);
,
前偶(奇)后奇(偶),join(left - 1 + N, right);
。
希望我的理解没有毛病,如果有的话,记住拍砖啊~~~
代码
#include <iostream>
#include <cstdio>
const int MAXN = 1e5 + 10;
int pre[MAXN * 2];
int find(int x)
{
if (pre[x] != x)
{
pre[x] = find(pre[x]);
}
return pre[x];
}
void join(int x, int y)
{
x = find(x);
y = find(y);
pre[y] = x;
}
void init()
{
for (int i = 0; i <= MAXN * 2; i++)
{
pre[i] = i;
}
}
int main(int argc, const char * argv[])
{
// freopen("/Users/zyj/Desktop/input.txt", "r", stdin);
init();
int N, Q;
std::cin >> N >> Q;
int left, right;
int cnt = 0, flag = -1;
char s[5];
while (++cnt)
{
if (cnt > Q)
{
break;
}
scanf("%d%d%s", &left, &right, s);
if (s[0] == 'e')
{
if (find(left - 1 + N) == find(right) && find(left - 1) == find(right + N))
{
flag = cnt;
break;
}
else
{
join(left - 1, right);
join(left - 1 + N, right + N);
}
}
else
{
if (find(left - 1) == find(right) && find(left - 1 + N) == find(right + N))
{
flag = cnt;
break;
}
else
{
join(left - 1, right + N);
join(left - 1 + N, right);
}
}
}
std::cout << flag << '\n';
return 0;
}