【POJ 1733】Parity game【带权并查集维护奇偶】

题意:

一个长度为 N N N 的由 0 0 0 1 1 1 组成的序列 S S S M M M 个询问,返回 [ l , r ] [l,r] [l,r] 1 1 1 的个数是奇数还是偶数。输出一个最小的 k k k,使得存在一个 01 01 01 序列满足第 1   k − 1 1~k-1 1 k1 个回答,但不存在满足第 1   k 1~k 1 k 个回答的 01 01 01 序列。
( N ≤ 1 0 9 , M ≤ 10000 ) (N\leq 10^9,M\leq 10000) (N109,M10000)


思路:

N N N 比较大,但是询问比较少,因此需要将所有数字离散化。

然后我们需要处理区间 [ l , r ] [l,r] [l,r] 1 1 1 的个数是偶数还是奇数。因此我们来定义一下 x → r o o t x\rightarrow root xroot d [ x ] d[x] d[x] 表示 s u m [ r o o t ] − s u m [ x ] sum[root]-sum[x] sum[root]sum[x] 的值为奇数还是偶数,即 [ x + 1 , r o o t ] [x+1,root] [x+1,root] 1 1 1 的个数为奇数还是偶数。如果维护的是 [ x , r o o t ] [x,root] [x,root] 的信息,那么 d [ x ] d[x] d[x] 初始值就不确定了,因此维护的是 [ x + 1 , r o o t ] [x+1,root] [x+1,root] d [ x ] d[x] d[x] 初始为 0 0 0

所以 x → r o o t x\rightarrow root xroot 0 0 0,表示 [ x + 1 , r o o t ] [x+1,root] [x+1,root] 1 1 1 的个数为偶数。如果为 1 1 1,则表示 1 1 1 的个数为奇数。在合并时维护一个模 2 2 2 剩余系。

比如 [ x , y ] [x,y] [x,y] 1 1 1 的个数为偶数,则 ( x − 1 ) → y (x-1)\rightarrow y (x1)y 0 0 0 f a [ f x ] = f y , d [ f x ] = ( x → y ) − ( x → f x ) − ( f y → y ) = ( 0 − d [ x ] + d [ y ] + 2 )   m o d   2 fa[fx] = fy,d[fx] = (x\rightarrow y)-(x\rightarrow fx)-(fy\rightarrow y)=(0-d[x]+d[y]+2)\ mod\ 2 fa[fx]=fyd[fx]=(xy)(xfx)(fyy)=(0d[x]+d[y]+2) mod 2

查询 [ x , y ] [x,y] [x,y] 1 1 1 个数奇偶,直接 ( d [ x ] − d [ y ] + 2 )   m o d   2 (d[x]-d[y]+2)\ mod \ 2 (d[x]d[y]+2) mod 2,为 0 0 0 则为偶,否则为奇。

至此即可完成本题。

ps:本题也可以维护并查集路径上节点的异或和, 0 0 0 表示为偶, 1 1 1 表示为奇。


代码:

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#define __ ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define rep(i,a,b) for(int i = a; i <= b; i++)
#define LOG1(x1,x2) cout << x1 << ": " << x2 << endl;
#define LOG2(x1,x2,y1,y2) cout << x1 << ": " << x2 << " , " << y1 << ": " << y2 << endl;
typedef long long ll;
typedef double db;
const db EPS = 1e-9;
using namespace std;
const int N = 2*1e4+100;

int n,m,fa[N],d[N],a[N],tot;
struct Query{
	int x,y,op;
}q[N];

int find(int x){
	if(x == fa[x]) return x;
	int root = find(fa[x]);
	d[x] = (d[x]+d[fa[x]]+2)%2;
	return fa[x] = root;
}

int search_for(int x){
	return lower_bound(a+1,a+1+tot,x)-a;
}

int main()
{
	scanf("%d%d",&n,&m); tot = 0;
	rep(i,1,m){
		scanf("%d%d",&q[i].x,&q[i].y);
		char oop[10]; scanf("%s",oop);
		if(oop[0] == 'e') q[i].op = 1;	//偶数
		else q[i].op = 0; //奇数
		a[++tot] = q[i].x-1, a[++tot] = q[i].y;
	}
	sort(a+1,a+1+tot);
	tot = unique(a+1,a+1+tot)-a-1;
	rep(i,0,tot) fa[i] = i, d[i] = 0;
	int ans = m;
	rep(i,1,m){
		int xx = search_for(q[i].x-1), yy = search_for(q[i].y), cc = q[i].op;
		int fx = find(xx), fy = find(yy);
		if(cc){	//偶数
			if(fx != fy){
				fa[fx] = fy;
				d[fx] = (d[yy]-d[xx]+2)%2;
			}
			else{
				int jud = (d[xx]-d[yy]+2)%2;
				if(jud == 1){
					ans = i-1;
					break;
				}
			}
		}
		else { 	//奇数
			if(fx != fy){
				fa[fx] = fy;
				d[fx] = (d[yy]-d[xx]+2+1)%2;
			}
			else{
				int jud = (d[xx]-d[yy]+2)%2;
				if(jud == 0){
					ans = i-1;
					break;
				}
			}
		}
	}
	printf("%d\n",ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Gene_INNOCENT

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值