题意:给你一个n,表示有一串长为n的由0和1组成的串,之后给你m次操作 a b op ,其中op表示是even 表示a到b的和是偶数,o表示的是奇数,现在问你在第几次操作的时候你发现他和之前矛盾。
前置技能:讲道理知道题的所有做法和公式都和之前的带权并查集一样,手推出如何压缩路径,如何合并,如何查找矛盾这题就可解了,唯一不同的是,这道题的区间有1到1000000000,需要离散化,我在这里讲一下离散化吧
比如当你的区间只有 1~9,1000~2000,10000~40000 ,的时候你会发现中间有好多我们是空着的用不到的,那我们把这些区间端点存一下拍一下序之后 映射一下 映射的值是他是他在当前序列的排名,1映射值就是1,9映射值就是2,1000映射值就是3,之后我们把区间变成了 1~2 , 2 ~3 ,3 ~4这种完全连续的区间了,省下了大量的空间。
思路:和前几题一样,没差了,就是离散需要学一下,上代码吧 ,代码上讲的很清楚:
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <map>
#include <iostream>
using namespace std;
const int maxn = 50000+10;
int p[maxn],Rank[maxn],ans , num[maxn];
struct node
{
int u,v,w;
}edg[maxn];
int getf(int x)
{
if(p[x] == x)
{
return x;
}
int t = p[x];
p[x] = getf(p[x]);
Rank[x] = (Rank[x] + Rank[t]) % 2;
return p[x];
}
void init()
{
for(int i = 0 ; i < maxn ; i++)
{
p[i] = i;
Rank[i] = 0;
num[i] = 0;
}
}
int main()
{
int n,m;
char op[10];
while(scanf("%d%d",&n,&m)!=EOF)
{
int cnt = 0;
init();
ans = 0 ;
for(int i = 0 ; i < m ;i++)
{
scanf("%d%d%s",&edg[i].u,&edg[i].v,op);
if(op[0] == 'e') edg[i].w = 0;
else edg[i].w = 1;
edg[i].u--;
num[cnt++] = edg[i].u;
num[cnt++] = edg[i].v;//我们把区间存起来
}
sort(num,num+cnt);//按照从小到大去排序
int flag = 0 ;
cnt = unique(num,num+cnt) - num;//去掉重复元素
for(int i = 0 ; i < m ; i++)
{
int nx = lower_bound(num,num+cnt,edg[i].u) - num;//二分去找当前的区间左端点在num数组中的排名
int ny = lower_bound(num,num+cnt,edg[i].v) - num;//二分去找当前的区间右端点在num数组中的排名
int dx = getf(nx);
int dy = getf(ny);
if(dx != dy)
{
p[dy] = dx;
Rank[dy] = (Rank[nx] - Rank[ny] + edg[i].w + 2) % 2;
}
else
{
if((Rank[ny] - Rank[nx] + 2)%2 != edg[i].w)//如果不一样就跳出了
{
flag = 1;
printf("%d\n",i);
break;
}
}
}
if(!flag) printf("%d\n",m);
}
}