奇偶数

题目描述

TomBob在玩一个游戏:他写一个由01组成的序列。

Tom选其中的一段(比如第3位到第5位),问他这段里面有奇数个1还是偶数个1Bob回答你的问题,然后Tom继续问。

Bob有可能在撒谎。Tom要检查Bob的答案,指出在Bob的第几个回答一定有问题。

有问题的意思就是存在一个01序列满足这个回答前的所有回答,而且不存在序列满足这个回答前的所有回答及这个回答。

输入

1行一个整数,是这个01序列的长度(<=1000000000)

2行一个整数,是问题和答案的个数。

3行开始是问题和答案,每行先有两个整数,表示你询问的段的开始位置和结束位置。

然后是Bob的回答。odd表示有奇数个1even表示有偶数个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个问题

乍一看,我们的心中就涌出一股熟悉感——发现和《谁在说谎》很像,于是我们就会想到这不是水题吗?关系类并查集啊。

可是再乍一看这道题的数据范围,发现数据范围非常诡异。n居然有10^9!感觉日了狗了。。。

尼玛,这怎么开数组,你在逗我……

可是我们再乍一看,发现m的范围非常正常(心中涌出一股感激之情)。我们一霎那(十分钟)就想到了离散化这种好东西。

有了它,我们可以秒过这种难倒ty大婶的水题。(ty冲了过来)

 我们发现这道题可以将相邻的点给合并,但是我们将数组离散化之后就没法判断是否相邻了,怎么办呢,我们需要请教ty大婶(ty摔了一跤).

口胡,并查集先生可以轻松妙杀ty(掏出Mr并查集,ty惊恐地爆0了,^_^)

咳咳,我们发现其实当区间相邻时,可以利用另外一个性质,它们的首尾端点相同!

我们只要不记格子,直接记端点即可.

轻松秒过啊.(ty:我不服!!Mr并查集:一砖排飞)



这里附代码(温馨提示,为防止某人不劳而获,我把程序修改了一下)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
using namespace std;
long long n,m,x,y,t,dis[100005],f[100005];
char st[100000];
struct node
{
     long long xx,yy,zz;
}a[100005],b[100005];
bool cmp(node x,node y)
{
     return x.xx<y.xx;
}
long long find( long long root)
{
     if (root==f[root]) return root;
     long long t=f[root];
     f[root]=find(f[root]);
     dis[root]+=dis[t];
     return f[root];
}
int main()
{
     cin>>n;
     cin>>m;
     for ( int i=1;i<=m;++i)
     {
         cin>>x>>y;
         cin>>st;
         if (st[0]== 'e' ) t=0; else t=1;
         x--;
         a[i+i-1].xx=x;a[i+i].xx=y;
         a[i+i-1].yy=i+i-1;a[i+i].yy=i+i;
         a[i+i-1].zz=t;a[i+i].zz=t;
     }
     sort(a+1,a+1+m+m,cmp);
     //for (int i=1;i<=m+m;++i) cout<<a[i].xx<<' ';cout<<endl;
     t=0;
     for ( int i=1;i<=m+m;++i)
     {
         if (a[i].xx!=a[i-1].xx) ++t;
         if (a[i].yy & 1) b[(a[i].yy+1)/2].xx=t,b[(a[i].yy+1)/2].zz=a[i].zz;
         else b[a[i].yy/2].yy=t,b[a[i].yy/2].zz=a[i].zz;
     }
     //for (int i=1;i<=m;++i) cout<<b[i].xx<<' '<<b[i].yy<<' '<<b[i].zz<<endl;
     n=t;
     for ( int i=1;i<=n;++i) f[i]=( long long )(i);
     memset (dis,0LL, sizeof (dis));
     for ( int i=1;i<=m;++i)
     {
         x=find(b[i].xx),y=find(b[i].yy);
         if (x==y)
         {
             if (((dis[b[i].xx]+dis[b[i].yy])&1) != b[i].zz) {cout<<i-1<<endl; return 0;}
             continue ;
         }
         else
         {
             f[y]=x;
             dis[y]=(b[i].zz+dis[b[i].yy]+dis[b[i].xx])&1;
         }
     }
     cout<<m<<endl;
     return 0;
}
总结:Mr并查集是ty的天敌。

题目不要乍一看,要乍三看。

有时记端点比记坐标来的方便。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值