【并查集】奇偶游戏
题目:
你和你的朋友玩一个游戏。你的朋友写下来一连串的0或者1。你选择一个连续的子序列然后问他,
这个子序列包含1的个数是奇数还是偶数。你的朋友回答完你的问题,接着你问下一个问题。
你怀疑你朋友的一些答案可能是错误的,你决定写一个程序来帮忙。程序将接受一系列你的
问题及你朋友的回答,程序的目的是找到第一个错误的回答i,也就是存在一个序列满足前i-1个问
题的答案,但是不满足前i个问题。
输入:
输入第一行有一个整数L(L<=1000000000),是这个01序列的长度。第二行是一个整数N(N<=5000),
是问题及其答案的数目,接下来N行描述问题和答案。每一行包含一个问题和这个问题的答案:
两个整数(子序列的起始位置和结束位置)和一个单词‘even’或者‘odd’,‘even’表示这
个子序列中的‘1’的个数是偶数,‘odd’则表示是奇数。
输出:
输出中只需输出一行一个整数X。表示存在一个01序列满足前面的X个问题,但是不存在一个01序列满
足前X+1个问题,如果存在一个序列满足所有问题,则输出N。
首先想到的是暴力去枚举字符串的状态再逐一判断
这种方法不仅思路复杂,而且效率非常低
以下是正解(并查集)
我们首先要知道,l~r的区间的和如何表示
设有一个前缀和f
那么就可以表示为f[r]-f[l-1]
根据这个原理,我想到:
每次操作从r向l-1连一条边,并根据输入确定边权(奇为1,偶为0)
每次寻找父亲的同时压缩路径,累加边权
至于长度太大的处理方法,可以使用离散化
我看n比较小,于是只开了一个列表去存储
每次查询一个值的复杂度也不会太高
标程:(仅供参考,请勿抄袭)
var b:array[0..10000]of longint;
father:array[0..10000,1..2]of longint;
l,n,i,j,x,y,x1,y1,x2,y2,k,ans:longint;
st:string;
function lookfor(t:longint):longint;
var i,x:longint;
begin
x:=0;
for i:=1 to b[0] do
if b[i]=t then
begin
x:=i;
break;
end;
if x<>0 then exit(x)
else
begin
inc(b[0]);
b[b[0]]:=t;
exit(b[0]);
end;
end;
function find(t:longint):longint;
begin
k:=(k+father[t,2])mod 2;
if father[t,1]=t then exit(t)
else exit(find(father[t,1]));
end;
begin
readln(l);
readln(n);
ans:=n;
for i:=1 to 10000 do
father[i,1]:=i;
for i:=1 to n do
begin
readln(x,y,st);
x1:=lookfor(x-1);
y1:=lookfor(y);
k:=0;
x2:=find(x1);
father[x1,1]:=x2;
father[x1,2]:=k;
k:=0;
y2:=find(y1);
father[y1,1]:=y2;
father[y1,2]:=k;
if x2<>y2 then
begin
father[y2,1]:=x2;
if st=' even' then
father[y2,2]:=(father[y1,2]-father[x1,2]+2)mod 2
else
father[y2,2]:=(father[y1,2]-father[x1,2]+3)mod 2
end
else
begin
if st=' even' then
begin
if(father[x1,2]<>father[y1,2]) then
begin
ans:=i-1;
break;
end
end
else
if(father[y1,2]-father[x1,2]+2)mod 2<>1 then
begin
ans:=i-1;
break;
end;
end;
end;
writeln(ans);
end.