POJ 1733 Parity game(路径压缩并查集+离散化)
http://poj.org/problem?id=1733
题意:
有一个由0或1组成的长度为N的串,现在给你K条该串的信息如:
1 2 even 表示连续字符区间[1,2]之间的字符有偶数个1。
3 4 odd 表示连续字符区间[3,4]之间的字符有奇数个1。
现在问你给的K条信息是否存在矛盾。假设存在矛盾且第一条矛盾信息为第i+1条,那么请输出i(即输出最大的前i条不矛盾的信息的这个最大i)。
分析:
首先对于每个输入的区间[u,v],我们把他改成(u-1,v]这个半开半闭的区间。然后令u-1和v做为并查集中的两个个节点。并查集每个节点维护下列信息:
F[i]:表示i节点的父节点编号(父节点编号>i)。
S[i]:表示i节点到其父节点的这个半开半闭区间(i,F[i]]内的1的个数(0表偶数个,1表奇数个)。
通过findset(u)我们可以让u直接连在根节点下面,并且更新S[u]值表示(u,根]这个区间1的属性。
如果u-1与v此时不在同一个分量中,那么我们可以知道(u-1,F[u-1]]区间和(v,F[v]]区间1的属性且我们知道(u-1,v]区间的1属性,那么我们可以推算出(F[u-1],F[v]](假设F[u-1]<F[v])区间的属性,进而合并F[u-1]与F[v]的连通分量。注意:合并两个连通分量的时候,我们始终保持编号大的节点作为父亲,编号小的节点作为儿子(虽然下面的代码并没有遵照这个约定,因为就算随便合并也可以,但是需要验证较多情况)。
至此,本题的并查集部分就完成了。
由于只有5000条语句,但是却有最大a为10亿,我们并查集没必要去申请10亿的数组。所以我们将预先读入所有输入数据对(u,v,type)且存储(u-1,v,type)这种三元组,然后将所有的u-1和v排序重新从1到X编号(即用map重新映射到数轴上)。然后用上面分析的并查集处理即可。
举例如下:
原始输出为:[2 8] even [2 7] odd
转换之后为:(1,8] even (1,7] odd
重新映射之后为:(1,3] even (1,2] odd
原来区间(7,8]之间为odd,现在同样区间(2,3]之间为odd。(2,3]区间不是指整数2到整数3之间而是代值第2个端点(值7)到到第3个端点(值8)之间为odd。
AC代码:110ms 由map实现哈希表
<span style="font-size:18px;">#include<cstdio> #include<cstring> #include<map> using namespace std; const int MAXN=10000+100; int F[MAXN]; int S[MAXN]; int findset(int i) { if(F[i]==-1)return i; int temp = findset(F[i]); S[i] =S[i]^S[F[i]]; return F[i]=temp; } map<int ,int>mp; int tot=0; int insert(int x) { if(mp.find(x)==mp.end())mp[x]=tot++; return mp[x]; } int main() { int n,m,k; while(scanf("%d",&n)==1) { memset(F,-1,sizeof(F)); memset(S,0,sizeof(S)); scanf("%d",&m); k=m; for(int i=0; i<m; i++) { int u,v; char str[10]; scanf("%d%d%s",&u,&v,str); if(k<m)continue; u=insert(u-1); v=insert(v); int temp; if(str[0]=='e')//获取(u,v]之间的奇偶性 temp=0; else if(str[0]=='o') temp=1; int fu=findset(u); int fv=findset(v); if(fu==fv) { int t = S[u]^S[v]; if(t!=temp) k=min(k,i); } else if(fu!=fv) { F[fu]=fv; S[fu]=S[u]^S[v]^temp; } } printf("%d\n",k); } return 0; }</span>
AC代码: 32ms由HASHMAP实现
<span style="font-size:18px;">#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int MAXN=10000+100; int F[MAXN]; int S[MAXN]; int findset(int i) { if(F[i]==-1)return i; int temp = findset(F[i]); S[i] =S[i]^S[F[i]]; return F[i]=temp; } /* //********哈希模板①********* #include<map> map<int ,int>mp; int tot=0; int insert(int x) { if(mp.find(x)==mp.end())mp[x]=tot++; return mp[x]; } //********哈希模板①********* */ //********哈希模板②********* const int HASH=10007; struct HASHMAP { int head[HASH]; int next[MAXN]; int state[MAXN]; int size; void init() { memset(head,-1,sizeof(head)); size=0; } int push(int st) { int h=st%HASH; for(int i=head[h];i!=-1;i=next[i]) { if(state[i]==st) { return i; } } state[size]=st; next[size]=head[h]; head[h]=size++; return size-1; } }hm; //********哈希模板②********* int main() { int n,m,k; while(scanf("%d",&n)==1) { hm.init(); memset(F,-1,sizeof(F)); memset(S,0,sizeof(S)); scanf("%d",&m); k=m; for(int i=0; i<m; i++) { int u,v; char str[10]; scanf("%d%d%s",&u,&v,str); if(k<m)continue; u=hm.push(u-1); v=hm.push(v); int temp; if(str[0]=='e')//获取(u,v]之间的奇偶性 temp=0; else if(str[0]=='o') temp=1; int fu=findset(u); int fv=findset(v); if(fu==fv)//相同连通分量,求值 { int t = S[u]^S[v]; if(t!=temp) k=min(k,i); } else if(fu!=fv)//不同连通分量,合并 { F[fu]=fv; S[fu]=S[u]^S[v]^temp; } } printf("%d\n",k); } return 0; } </span>