even

第一行给你一个n(<=10^10)表示有一个长度为n的0,1字符串,
第二行给你一个m(<=5000)表示接下来有m行输入,
接下来的m行输入中
x, y, even表示第x到第y个字符中间1的个数为偶数个,
x, y, odd表示第x到第y个字符中间1的个数为奇数个,
若m句话中第k+1是第一次与前面的话矛盾, 输出k;否则输出m

Sample Input
10  //长度为10的字符串
5  //5个询问
1 2 even  //区间1-2有偶数个1
3 4 odd  //区间3-4有奇数个1
5 6 even
1 6 even
7 10 odd
Sample Output
3  //第4个询问与前3个询问矛盾

sol:本题用并查集开虚点的方法解决,类似犯罪团伙。

根据读入提取信息:某区间1的个数的奇偶性,用区间部分和表示。用sum[i]来表示前i个数的和,由于每一位是0、1数字,sum[i]为多少,表示前i位有多少个1。

若区间[i,j]1的个数为偶数,即sum[j]-sum[i-1]的值为偶数,则说明sum[j]与sum[i-1]的奇偶性一致,因为奇数-奇数得偶数,偶数-偶数得偶数,于是将i,j放在同一个集合。与i,j奇偶性相反的i’与j’应在同一集合。

若区间[i,j]1的个数为奇数,即sum[j]-sum[i-1]的值为奇数,则说明sum[j]与sum[i-1]的奇偶性相反,因为奇数-偶数得奇数,偶数-奇数得奇数,于是将i,j放在不同的集合。与i奇偶性相反的i’与j应在同一集合,与j奇偶性相反的j’与i应在同一集合。

另外,本题给出的n的值很大,是10^10,我们做并查集时要记录每一个结点的父亲结点,father数组需要开2*10^10那么大,超内存空间,而我们发现m的值不大,才5000内,就算每个区间的左右端点都不一样,也才10000个点,开虚点再翻倍,并且本题我们在做并查集连边时,只要知道左右端点的关系,是否在同一个集合中,跟两端点具体是什么值无关,因此我们可以做离散化的操作。将每个区间的左右端点映射成较小的数。

代码实现如下:

 1 #include <cstdio>  
 2 #include <cstdlib>  
 3 #include <cstring>  
 4 #include <algorithm>  
 5 #include <cmath>  
 6 #include <stack>  
 7 #include <queue>  
 8 #include <vector>  
 9 #include <string>  
10 #include <climits>  
11 #include <iostream>  
12 #define INF 0x3f3f3f3f  
13 #define ll long long 
14 #define dd double 
15 using namespace std;
16 int dad[20100],m,n,a[20010],t;
17 char st[20100];
18 struct node
19 {
20     int l,r,ty;
21 }que[10010];
22 int getf(int x)
23 {
24     return x==dad[x]?x:dad[x]=getf(dad[x]);
25 }
26 int main()
27 {
28     scanf("%d",&m);
29     scanf("%d",&n);
30     for (int i=1;i<=n;i++)
31     {
32         scanf("%d%d%s",&que[i].l,&que[i].r,st);
33         que[i].ty=(st[0]=='o'?1:0);//如果是奇数就TRUE,偶数就FALSE 
34         a[++t]=que[i].l-1,//记下左边界 
35         a[++t]=que[i].r;//记下右边界 
36     }
37     sort(a+1,a+t+1);//这个是做离散化用的 
38     m=unique(a+1,a+t+1)-a-1;//算出a数组中有M种不同的数字 
39     for (int i=1;i<=2*m;i++) //父亲点要开2倍  
40          dad[i]=i;
41     for (int i=1;i<=n;i++)//n是询问数 
42     {
43         int x=lower_bound(a+1,a+m+1,que[i].l-1)-a;
44         int y=lower_bound(a+1,a+m+1,que[i].r)-a;
45         int xx=x+m;//找出各自的对立点 
46         int yy=y+m;//找出各自的对立点
47         if (que[i].ty==1)  //如果这个记录是TRUE,则说x,y两个点的奇偶性是不一样的
48         {
49             if (getf(x)==getf(y)) //但是它们居然在同一个集合中,说明出错
50             {
51                 printf("%d\n",i-1);
52                 return 0;
53             }
54             dad[getf(x)]=getf(yy);//如果前面没有退出,则将x与yy(y的对立面)放在一个集合中
55             dad[getf(xx)]=getf(y);//同上
56         }
57         else   //说明这个记录是FALSE,则说x,y两个点的奇偶性是一样的
58         {
59             if (getf(x)==getf(yy)) //但是它们居然在同一个集合中,说明出错
60             {
61                 printf("%d\n",i-1);
62                 return 0;
63             }
64             dad[getf(x)]=getf(y);//如果前面没有退出,则将x与y放在一个集合中
65             dad[getf(xx)]=getf(yy);
66         }
67     }
68     printf("%d\n",n);
69     return 0;
70 }

猜你喜欢

转载自www.cnblogs.com/cutepota/p/12557997.html