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>