P5937 [CEOI1999]Parity Game-扩展域并查集与离散化处理

题目链接:洛谷P5937
这道题需要用到种类并查集+离散化+前缀和。
对于100%的数据,1≤N≤109,1≤M≤5×103
因为N有点大,所以我们需要使用到离散化! 来将大数据映射到小范围的小数据。
先前对离散化没有了解的可以学习此篇文章:离 散 化
总得来说离散化有三步走战略:
1.去重(可以用到unique去重函数)
2.排序
3.二分索引(可以用到lower_bound函数)

题解:由于数列中只有0或1,那么使先开一个s数组表示前缀和,那么: s[j]-s[i-1] 是奇数时,则[2 … j]区间中的1的个数为奇数,反之,为偶数。
f[i]存和自己奇偶性相同的集合,f[i+n]存和自己奇偶性不同的集合;
可以根据 奇+奇=偶;偶+偶=偶;奇+偶=奇;来判断一段区间的奇偶性。

#include<bits/stdc++.h>
using namespace std;
#define maxn 5005

struct{
	int lc,rc,op;
}a[maxn];
int b[maxn*2],cnt;//离散化
int f[maxn*2];//存祖先

int find(int x){
	if(f[x]!=x){
		f[x]=find(f[x]);
	}
	return f[x];
}

void hb(int x,int y){	
	f[find(x)]=find(y);
}

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int n,m;
	string sh;
	cin >> n >> m;
	for(int i=1;i<=m;i++){
		cin >> a[i].lc >> a[i].rc >> sh;
		a[i].lc--;//先--,方便前缀和的计算
		if(sh=="even"){
			a[i].op=1;//偶数
		}else{
			a[i].op=0;
		}
		b[++cnt]=a[i].lc;
		b[++cnt]=a[i].rc;
	}
	sort(b+1,b+cnt+1);
	int res=unique(b+1,b+cnt+1)-b-1;
	for(int i=1;i<=2*res;i++){
		f[i]=i;
	}
	for(int i=1;i<=m;i++){
		a[i].lc=lower_bound(b+1,b+res+1,a[i].lc)-b;
		a[i].rc=lower_bound(b+1,b+res+1,a[i].rc)-b;
		if(a[i].op==1){
			//偶数:奇+奇/偶+偶,所以如果(左端点--)后的前缀和和右端点的敌方阵营相同,也就是(左端点--)的前缀和和右端点的奇偶性不同,那么相减后(也就是左端点~右端点)一定为奇数个
			if(find(a[i].lc)==find(a[i].rc+res)){
				cout << i-1;
				return 0;
			}else{
				hb(a[i].lc,a[i].rc);
				hb(a[i].lc+res,a[i].rc+res);
			}
		}else{
			//奇数:奇+偶,同上。
			if(find(a[i].lc)==find(a[i].rc)){
				cout << i-1;
				return 0;
			}else{
				hb(a[i].lc,a[i].rc+res);
				hb(a[i].lc+res,a[i].rc);
			}
			
		}
	}
	cout << m;
	return 0;
}
  • 6
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值