离散化 边带权/扩展域的并查集

Training 4 - B题

Now and then you play the following game with your friend. Your friend writes down a sequence consisting of zeroes and ones. You choose a continuous subsequence (for example the subsequence from the third to the fifth digit inclusively) and ask him, whether this subsequence contains even or odd number of ones. Your friend answers your question and you can ask him about another subsequence and so on. Your task is to guess the entire sequence of numbers.

You suspect some of your friend’s answers may not be correct and you want to convict him of falsehood. Thus you have decided to write a program to help you in this matter. The program will receive a series of your questions together with the answers you have received from your friend. The aim of this program is to find the first answer which is provably wrong, i.e. that there exists a sequence satisfying answers to all the previous questions, but no such sequence satisfies this answer.

Input

The first line of input contains one number, which is the length of the sequence of zeroes and ones. This length is less or equal to 1000000000. In the second line, there is one positive integer which is the number of questions asked and answers to them. The number of questions and answers is less or equal to 5000. The remaining lines specify questions and answers. Each line contains one question and the answer to this question: two integers (the position of the first and last digit in the chosen subsequence) and one word which is either even or odd (the answer, i.e. the parity of the number of ones in the chosen subsequence, where even means an even number of ones and odd means an odd number).

Output

There is only one line in output containing one integer X. Number X says that there exists a sequence of zeroes and ones satisfying first X parity conditions, but there exists none satisfying X+1 conditions. If there exists a sequence of zeroes and ones satisfying all the given conditions, then number X should be the number of all the questions asked.

Sample Input

10
5
1 2 even
3 4 odd
5 6 even
1 6 even
7 10 odd

Sample Output

3

法1:


#pragma warning (disable:4996)
#include <iostream>
#include <algorithm>
#include <iomanip>
#include <cstring>
#include <string>
#include <cstdio>
#include <cmath>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define inf 0X3f3f3f3f
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N = 2e4 + 5;
const int maxn = 1e4 + 5;

int fa[N];
int d[N];
int s[N];

void initial(int n)
{
	for (int i = 1; i <= n; i++)
	{
		fa[i] = i;
		d[i] = 0;
		s[i] = 1;
	}
}

int get(int x)
{
	if (x == fa[x])
		return x;
	int root = get(fa[x]);
	d[x] ^= d[fa[x]];//对边权求异或
	return fa[x] = root;
}

void merge(int x, int y)
{
	//x接在y之后
	x = get(x);
	y = get(y);
	fa[x] = y;
	d[x] = s[y];//对根节点d的值初始化
	s[y] += s[x];
}

struct
{
	int l, r;
	bool ans;
}q[maxn];
int a[N], b[N];

int query(int r, int x)
{
	return lower_bound(b + 1, b + r + 1, x) - b;
}

int main()
{
	int n, m;
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= m; i++)
	{
		int l, r;
		string str;
		scanf("%d%d", &q[i].l, &q[i].r);
		cin >> str;
		if (str == "even")
			q[i].ans = 0;
		if (str == "odd")
			q[i].ans = 1;
		a[i] = q[i].l - 1;
		a[i + m] = q[i].r;
	}
	sort(a + 1, a + 2 * m + 1);
	int index = 0;
	for (int i = 1; i <= 2 * m; i++)
		if (i == 1 || a[i] != a[i - 1])
			b[++index] = a[i];
	for (int k = 1; k <= m; k++)
	{
		q[k].l = query(index, q[k].l - 1);
		q[k].r = query(index, q[k].r);
	}
	initial(index);
	for (int i = 1; i <= m; i++)
	{
		int  l = get(q[i].l), r = get(q[i].r);
		if (l == r)
		{
			if (d[q[i].l] ^ d[q[i].r] != q[i].ans)
			{
				printf("%d\n", i - 1);
				return 0;
			}
		}
		else
		{
			fa[l] = r;
			d[l] = d[q[i].l] ^ d[q[i].r] ^ q[i].ans;
		}
	}
	printf("%d\n", m);
	return 0;
}

思路:
①对前缀和数组sum[i]来说,l,r之间有偶数个1:sum[l-1]和sum[r]的奇偶性相同;l,r之间有奇数个1:sum[l-1]和sum[r]的奇偶性不同。
②离散化,将出现过的所有数字映射在n的范围内。
③并查集,每个集合放入已知前缀和奇偶性相互关系的数字。对于两个数字x和y,如果在一个集合则判断他们之间的奇偶性符不符合描述;如果不在一个集合就将两个数放入一个集合。用路径权值的奇偶性表示到每个节点的奇偶性,通过异或运算实现。
可以用向量的方式理解:
在这里插入图片描述

法2:

#pragma warning (disable:4996)
#include <iostream>
#include <algorithm>
#include <iomanip>
#include <cstring>
#include <string>
#include <cstdio>
#include <cmath>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define inf 0X3f3f3f3f
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N = 2e4 + 5;
const int maxn = 1e4 + 5;

int fa[N];//n:偶 n+n:奇

void initial(int n)
{
	for (int i = 1; i <= n; i++)
	{
		fa[i] = i;
	}
}

int get(int x)
{
	if (x == fa[x])
		return x;
	return fa[x] = get(fa[x]);
}

void merge(int x, int y)
{
	fa[get(x)] = get(y);
}

struct
{
	int l, r;
	bool ans;
}q[maxn];
int a[N], b[N];

int query(int r, int x)
{
	return lower_bound(b + 1, b + r + 1, x) - b;
}

int main()
{
	int n, m;
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= m; i++)
	{
		int l, r;
		string str;
		scanf("%d%d", &q[i].l, &q[i].r);
		cin >> str;
		if (str == "even")
			q[i].ans = 0;
		if (str == "odd")
			q[i].ans = 1;
		a[i] = q[i].l - 1;
		a[i + m] = q[i].r;
	}
	sort(a + 1, a + 2 * m + 1);
	int index = 0;
	for (int i = 1; i <= 2 * m; i++)
		if (i == 1 || a[i] != a[i - 1])
			b[++index] = a[i];
	for (int k = 1; k <= m; k++)
	{
		q[k].l = query(index, q[k].l - 1);
		q[k].r = query(index, q[k].r);
	}
	initial(2 * index);
	for (int i = 1; i <= m; i++)
	{
		if (q[i].ans == 0)
		{
			if (get(q[i].l) == get(q[i].r + index) || get(q[i].l + index) == get(q[i].r))
			{
				printf("%d\n", i - 1);
				return 0;
			}
			merge(q[i].l, q[i].r);
			merge(q[i].l + index, q[i].r + index);
		}
		if (q[i].ans == 1)
		{
			if (get(q[i].l) == get(q[i].r) && get(q[i].l + index) == get(q[i].r + index))
			{
				printf("%d\n", i - 1);
				return 0;
			}
			merge(q[i].l, q[i].r + index);
			merge(q[i].l + index, q[i].r);
		}
	}
	printf("%d\n", m);
	return 0;
}

思路:
①对前缀和数组sum[i]来说,l,r之间有偶数个1:sum[l-1]和sum[r]的奇偶性相同;l,r之间有奇数个1:sum[l-1]和sum[r]的奇偶性不同。
②离散化,将出现过的所有数字映射在n的范围内。
③扩展域的并查集,n范围内表示当前奇偶性,n+n范围内表示另一种奇偶性。如果当前两个数x,y是奇偶性相同,判断x和y+n,y和x+n不在同一集合,将下x和y,x+n和y+n合并;如果当前两个数x,y是奇偶性不同,判断x和y,x+n和y+n不在同一集合,将x和y+n,x+n和y合并。

发布了28 篇原创文章 · 获赞 0 · 访问量 321

猜你喜欢

转载自blog.csdn.net/xukeke12138/article/details/104823524
今日推荐