1221: 奇偶数
题目描述
Tom和Bob在玩一个游戏:他写一个由0和1组成的序列。
Tom选其中的一段(比如第3位到第5位),问他这段里面有奇数个1还是偶数个1。Bob回答你的问题,然后Tom继续问。
Bob有可能在撒谎。Tom要检查Bob的答案,指出在Bob的第几个回答一定有问题。
有问题的意思就是存在一个01序列满足这个回答前的所有回答,而且不存在序列满足这个回答前的所有回答及这个回答。
输入
第1行一个整数,是这个01序列的长度(<=1000000000)
第2行一个整数,是问题和答案的个数。
第3行开始是问题和答案,每行先有两个整数,表示你询问的段的开始位置和结束位置。
然后是Bob的回答。odd表示有奇数个1,even表示有偶数个1。
输出
输出一行,一个数X,表示存在一个01序列满足第1到第X个回答,但是不存在序列满足第1到第X+1个回答。如果所有回答都没问题,你就输出所有回答的个数。
样例输入
10
5
1 2 even
3 4 odd
5 6 even
1 6 even
7 10 odd
样例输出
3
提示
【样例说明】
第4条与之前有矛盾,前3条没问题
【数据规模和约定】
最多10000个问题
题解:
看题目很容易想到并查集,但手动算了一下样例后发现,此题并没那么容易。。
这里的x,y指的是x-y区间内1的奇偶。普通的关系类并查集都是合并成同一个父亲
的。但这题的1,2和3,4实际上是可以合并为1,4的。。
我的解决方法是将1,2改为1,3,即x,y改为x,y+1,改变后的区间右端点实际上是不
取到的,这样一来问题也就容易了,当然由于01长度会很大,需要离散化。
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
struct node
{
int a,id;
}p[10005];
char s[10005][15];
int T,n,i,k,ans,x,y,fx,fy,a[10005],h[20005],f[20005],dis[20005];
bool cmp(const node&x,const node&y)
{
return x.a<y.a;
}
int get(int x)
{
if(f[x]==x) return x;else
{
int y=get(f[x]);
dis[x]=(dis[f[x]]+dis[x])%2;
return f[x]=y;
}
}
int main()
{
scanf("%d%d",&T,&n);
for(i=1;i<=n;i++)
{
scanf("%d%d%s",&p[i*2-1].a,&p[i*2].a,s[i]);
p[i*2].a++;
p[i*2-1].id=i*2-1;
p[i*2].id=i*2;
}
sort(p+1,p+2*n+1,cmp);
p[0].a=-1;
for(i=1;i<=2*n;i++)
{
if(p[i].a!=p[i-1].a) k++;
h[p[i].id]=k;
}
for(i=1;i<=2*n;i++)
f[i]=i;
for(i=1;i<=n;i++)
{
x=h[i*2-1];
y=h[i*2];
fx=get(x);
fy=get(y);
if(fx==fy)
{
if(s[i][0]=='o'&&(dis[x]-dis[y]+2)%2==0) break;
if(s[i][0]=='e'&&(dis[x]-dis[y]+2)%2==1) break;
}
if(s[i][0]=='o')
{
f[fy]=fx;
dis[fy]=(dis[x]+dis[y]+1)%2;
} else
{
f[fy]=fx;
dis[fy]=(dis[x]+dis[y])%2;
}
ans=i;
}
printf("%d",ans);
return 0;
}
bzoj1202狡猾的商人也是类似的做法,不过更为简单。。