1 /*
2 * poj-1733 Parity game.cpp
3 *
4 * Created on: 2012-2-17
5 * Author: LongDou
6 *
7 *
8 * hash离散化 + 并查集
9 *
10 * 设s[0]=0,s[i]=a[1]+a[2]+...+a[i],则信息i j even等价于a[i]+...+a[j]为偶数,即
11 s[j]-s[i-1]为偶数,即s[j]与s[i-1]同奇偶。这样,每条信息都可以变为
12 s[i-1]和s[j]是否同奇偶的信息。
13
14 若记:
15 same[j]为当前和s[j]同奇偶的元素集合,
16 diff[j]为和s[j]不同奇偶的元素集合,
17 则一条信息i j even将导致same[j]和same[i-1]合并,diff[j]和diff[i-1]合并;
18 信息i j odd将导致same[j]和diff[i-1]合并;diff[j]和same[i-1]合并。
19
20 另外无法直接开1000000000的数组,需离散化(用最简单的hash)
21 *
22 */
23
24 #include <cstdio>
25 #include <cstring>
26 using namespace std;
27
28 const int maxL = 10000 + 10;
29 const int MOD = 9941; //hash
30
31 int len, qnum;
32 //same[j]为s[j]的父节点,
33 //diff[j]为与s[j]不同奇偶的某元素(即指向与s[j]不同奇偶的元素集合)
34 int same[maxL], diff[maxL], rank[maxL];
35 int hash[maxL];
36
37 void init(){
38 for(int i=0; i<=maxL; i++){
39 same[i] = i;
40 diff[i] = -1;
41 rank[i] = 0;
42 hash[i] = -1;
43 }
44 }
45
46 int findSet(int x){
47 if(x == -1) return -1;
48 if(same[x] == x)
49 return x;
50
51 int tmp = same[x];
52 same[x] = findSet(tmp);
53
54 return same[x];
55 }
56
57 void unionSet(int x, int y){
58 if(x == -1 || y == -1) return;
59
60 int fx = findSet(x);
61 int fy = findSet(y);
62 if(fx == fy)
63 return;
64
65 if(rank[fx] < rank[fy])
66 same[fx] = fy;
67 else if(rank[fy] < rank[fx])
68 same[fy] = fx;
69 else{
70 same[fx] = fy;
71 rank[fy]++;
72 }
73 }
74
75 int main(){
76 scanf("%d%d", &len, &qnum);
77
78 init();
79
80 int a, b, sa, sb, da, db, ha, hb;
81 char ans[5];
82
83 //
84 for(int i=1; i<=qnum; i++){
85 scanf("%d%d%s", &a , &b, ans);
86 a--; //注意先减1
87
88 //离散化a
89 ha = a % MOD;
90 while(hash[ha] != -1 && hash[ha] != a)
91 ha = (ha + 1) % MOD;
92 hash[ha] = a;
93 a = ha;
94
95 //离散化b
96 hb = b % MOD;
97 while(hash[hb] != -1 && hash[hb] != b)
98 hb = (hb + 1) % MOD;
99 hash[hb] = b;
100 b = hb;
101
102 //各集合的代表元
103 sa = findSet(a);
104 da = findSet(diff[a]);
105 sb = findSet(b);
106 db = findSet(diff[b]);
107
108 if(!strcmp(ans, "even")){
109 if(sa == db || da == sb){
110 printf("%d\n", i-1);
111 return 0;
112 }
113 if(diff[a] == -1) diff[a] = db; //设置diff[]
114 if(diff[b] == -1) diff[b] = da;
115 unionSet(sa, sb); //合并
116 unionSet(da, db);
117 }
118 else if(!strcmp(ans, "odd")){
119 if(sa == sb || (da != -1 && da == db)){
120 printf("%d\n", i-1);
121 return 0;
122 }
123 if(diff[a] == -1) diff[a] = sb; //设置diff[]
124 if(diff[b] == -1) diff[b] = sa;
125 unionSet(sa, db); //合并
126 unionSet(da, sb);
127 }
128 }
129 //全部正确,输出问题数
130 printf("%d\n", qnum);
131
132
133 return 0;
134 }
//再转一个其他方法实现的, 仍是并查集, 有点类似poj-1703 Find them, Catch them 的方法,离散化的方法与上面类似
/*
解题思路:hash离散化+并查集
首先我们不考虑离散化:s[x]表示(root[x],x]区间1的个数的奇偶性,0-偶数,1-奇数
每个输入区间[a,b],首先判断a-1与b的根节点是否相同
a)如果相同表示(a-1,b]之间1的个数奇偶性已知s((a-1,b])=s[a-1]^s[b],此时只需简单判断即可
b)如果不同,我们需要合并两个子树,我们将root较大的子树(例root[a])合并到root较小的子树(例root[b]),且此时s[root[a]]=s[a]^s[b]^s((a-1,b])
在路径压缩的过程中s[i]=s[i]^s[root[i]],s[root[i]]为(root[root[i]], root[i]]区间内1个数的奇偶性,例(a, b]区间1的个数为偶数,(b, c]区间1的个数为奇数,(a, c]之间1的个数显然为0^1=1奇数
*/
#include <iostream>
using namespace std;
#define MOD 9941
#define MAX 10005
struct node
{
node():next(0){}
int val, index;
node* next;
}HashMap[MOD];
int root[MAX],num[MAX],u=0;
char c[MAX];
//hash离散化,找到x对应的离散化后的值
int find(int x)
{
int y = x%MOD;
node *p,*q;
p = &HashMap[y], q = HashMap[y].next;
while (q)
if(q->val == x)return q->index;
else p=q,q=q->next;
node *temp=new node(); temp->val=x,temp->index=++u,p->next=temp,num[u]=x;
return u;
}
//并查集-x的根结点
int findroot(int x)
{
int t;
if(root[x]!=x){t = root[x];root[x]=findroot(root[x]);c[x] = c[t]^c[x];}
return root[x];
}
int main()
{
int i,l,n,s,t,r1,r2,v,ans;
bool IsError = false;
char ch[5];
for(i=0;i<MAX;i++)root[i]=i,c[i]=0;
scanf("%d\n%d", &l, &n);
for (i=1; i<=n;i++)
{
scanf("%d %d %s", &s, &t, ch);
if(IsError)continue;
v = (ch[0] == 'e')?0:1;
s=find(s-1),t=find(t); //离散化后的值
r1=findroot(s), r2=findroot(t); //根结点
if(r1==r2 && (c[s]^c[t]^v)){IsError=true;ans=i-1;}
else if(r1 != r2)
{
if(num[r1]<num[r2])root[r2]=r1,c[r2]=c[s]^c[t]^v;
else root[r1]=r2, c[r1]=c[s]^c[t]^v;
}
}
IsError ? printf("%d\n",ans) : printf("%d\n", n);
return 0;
}