题目描述  
 
 
思路  
怎么得到这个序列中每一段的关系?  
我们可以把这个只包含0和1的序列看作一个数组,0表示当前位置为0,1表示当前位置为1,利用前缀和的性质可以知道某一段中所包含的1的数量 sum1 = a[r] - a[l-1] 如果sum1为偶数,那么a[r] 和 a[l-1]的奇偶性相同 如果sum1为奇数,那么a[r] 和 a[l-1]的奇偶性不同 找到它们之间的关系,我们就可以使用并查集来存储他们 为什么要离散化?  
序列的长度小于等于1e9,然而序列中下标出现的次数最多为1e4,所以使用离散化 种类并查集  
序列中某一段有两种关系,奇数个1、偶数个1 我们定义两个扩展域,1~n表示偶数,n + 1 ~ 2 * n表示奇数 每次知道一个关系之后,将偶数区域和奇数区域都进行处理,像枚举一样,不漏掉每一种情况 并查集中存储的是区域与区域之间的关系,如果有一个关系成立,那么这个集合中其它的关系都成立 代码实现  
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
const int N = 1e4 + 10;
int fa[2 * N];		// 两个扩展域:1~N表示偶数关系,N+1~2N表示奇数关系
int n, m;
vector<int> ans;	// 离散化数组
struct node	// 结构体,存储每一段区间的左右端点
{
	int x, y;
	string op;
}a[N];
int get(int u)	// 二分查找,将原数据离散化成下标
{
	int l = 0, r = ans.size();
	while(l + 1 != r)
	{
		int mid = l + r >> 1;
		if(ans[mid] < u) l = mid;
		else r = mid;
	}
	return r;
}
int find(int u)		// 返回当前元素的祖宗元素,在哪个集合中
{
	if(fa[u] != u) fa[u] = find(fa[u]);
	return fa[u];
}
void merge(int x, int y)	// 将x合并到y所在的集合中
{
	fa[find(x)] = find(y);
}
int main()
{
	cin >> n >> m;
	for(int i = 1; i <= m; i++)
	{
		cin >> a[i].x >> a[i].y >> a[i].op;	
		a[i].x--;	// 因为利用了前缀和的性质,所以要将左端点-1,满足sum = a[r] - a[l-1]
		ans.push_back(a[i].x);
		ans.push_back(a[i].y);
	}
	sort(ans.begin(), ans.end());	// 将数据进行排序
	ans.erase(unique(ans.begin(), ans.end()), ans.end());	// 进行去重
	ans.insert(ans.begin(), 0);	// 将离散化之后的下标从1开始
	for(int i = 1;i <= m; i++)	// 找到每个区间左右端点离散化之后的数据,使用新数据
	{
		a[i].x = get(a[i].x);
		a[i].y = get(a[i].y);
	}
	for(int i = 0;i < 2 * N; i++) fa[i] = i;	// 初始化集合,每一个元素在不同的集合中
	n = ans.size();	// 每个扩展域的范围
	for(int i = 1; i <= m; i++)	// 开始遍历每一个回答,判断左右端点集合中的关系
	{
		int x = a[i].x, y = a[i].y;
		string op = a[i].op;
		if(op == "even")	// 有偶数个1
		{
            // 只需要判断一种情况就可,因为他们的关系是对称的
			if(find(x) == find(y + n))	// x为偶数的集合中,存在y为奇数的关系,矛盾
			{
				cout << i - 1 << endl;
				return 0;
			}
            // x与y的奇偶性相同
			merge(x, y); 	// 将x为偶数,y为偶数放在一个集合中
			merge(x + n, y + n);	// 将x为奇数,y为奇数放在一个集合中
		}
		else	// 有奇数个1,那么x和y的奇偶性不同
		{
			if(find(x) == find(y))	// x为偶数的集合中,存在y为偶数的关系,矛盾
			{
				cout << i - 1 << endl;
				return 0;
			}
			merge(x, y + n);	// 将x为偶数,y为奇数放在一个集合中
			merge(x + n, y);	// 将x为奇数,y为偶数放在一个集合中
		}
	}
	cout << m << endl;
	
	return 0;
}权值并查集  
每一个区间的奇偶性有两种情况,奇数或者偶数 我们可以维护集合中每个区间的关系,这个关系可以用集合中的子区间到祖宗区间的距离来表示,因为有两种情况,所以当距离d[i] % 2 == 0时,当前区间的奇偶性和祖宗元素的奇偶性相同,d[i] % 2 ==1时,当前区间的奇偶性和祖宗元素的奇偶性不同 每一个集合中的区间相对于祖宗区间的奇偶性是已知的,所以集合中所有区间的奇偶性关系都是已知的 代码实现  
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
const int N = 1e5 + 10;
int fa[N];	// 初始化每一个集合都是独立的
int d[N];	// 集合中子区间到祖宗区间的距离,可以判断奇偶关系
vector<int> ans;	// 离散化数组
int n, m;
struct node	// 存储每一个回答
{
	int x, y;
	string op;
}a[N];
int get(int u)	// 二分查找进行离散化
{
	int l = 0, r = ans.size();
	while(l + 1 != r)
	{
		int mid = l + r >> 1;
		if(ans[mid] < u) l = mid;
		else r = mid;
	}
	return r;
}
int find(int u)	// 找到当前区间的祖宗区间,并进行路径压缩和更新到祖宗区间的距离
{
	if(fa[u] != u)
	{
		int t = find(fa[u]);
		d[u] += d[fa[u]];
		fa[u] = t;
	}
	return fa[u];
}
int main()
{
	cin >> n >> m;
	for(int i = 1;i <= m; i++)
	{
		cin >> a[i].x >> a[i].y >> a[i].op;
		a[i].x--;
		ans.push_back(a[i].x);
		ans.push_back(a[i].y);
	}
	sort(ans.begin(), ans.end());
	ans.erase(unique(ans.begin(), ans.end()), ans.end());
	ans.insert(ans.begin(), 0);
	for(int i = 1; i <= m; i++) 
	{
		a[i].x = get(a[i].x);
		a[i].y = get(a[i].y);
	}
	for(int i = 1; i <= ans.size(); i++) fa[i] = i;
	for(int i = 1;i <= m; i++)
	{
		int fx = find(a[i].x), fy = find(a[i].y);
		string op = a[i].op;
		if(op == "even")
		{
			if(fx != fy)
			{
				d[fx] = d[a[i].x] ^ d[a[i].y];
				fa[fx] = fy;
			}
			else 
			{
				if((d[a[i].x] + d[a[i].y]) % 2 != 0)
				{
					cout << i - 1 << endl;
					return 0;
				}
			}
		}
		else
		{
			if(fx != fy)
			{
				d[fx] = d[a[i].x] ^ d[a[i].y] ^ 1;
				fa[fx] = fy;
			}
			else
			{
				if((d[a[i].x] + d[a[i].y]) % 2 == 0)
				{
					cout << i - 1 << endl;
					
					return 0;
				}
			}
		}
	}
	cout << m << endl;
	
	return 0;
}