题目
https://jzoj.net/senior/#main/show/3453
小结
这是一道并查集的问题。
首先我们算出f[i],表示只取前i条边的每个点的父亲,换句话说,就是f[i,j]表示只取前i条边的第i个点的父亲。
然后按同样的办法算出g[i],表示只取后i个点每个点的状态,那么。
然后就可以在线做啦~
输入一个l,r,那么我们就可以把f[l-1]和g[r+1]的状态结合一次,在查找一下。
那么问题来了,我们要怎么合并?
比如,我们把f[l-1]的状态存到一个叫fg的数组里,这个数组时一维的,那么再看每一个点的父亲是否相同,如果不同就合并,这个很好理解吧,跟并查集的操作是一样的。
最后在找找他们的祖先不同的一共有多少个,就算出答案了,时间复杂度是O(nk);
var
n,m,i,j,xx,yy,ans,l,r,k:longint;
f,g:array[0..20000,0..500]of longint;
fg:array[0..500]of longint;
bz:array[0..500]of boolean;
x,y:array[0..20000]of longint;
function get(t,z:longint):longint;
begin
if f[z,t]=t then exit(t);
f[z,t]:=get(f[z,t],z);
exit(f[z,t]);
end;
function get2(t,z:longint):longint;
begin
if g[z,t]=t then exit(t);
g[z,t]:=get2(g[z,t],z);
exit(g[z,t]);
end;
function get3(t:longint):longint;
begin
if fg[t]=t then exit(t);
fg[t]:=get3(fg[t]);
exit(fg[t]);
end;
begin
assign(input,'connect.in');reset(input);
assign(output,'connect.out');rewrite(output);
readln(n,m);
for i:=1 to m do
readln(x[i],y[i]);
for i:=1 to n do
f[0,i]:=i;
for i:=1 to m do
begin
f[i]:=f[i-1];
l:=get(x[i],i);
r:=get(y[i],i);
if l<>r then
f[i,l]:=r;
end;
for i:=1 to n do
g[m+1,i]:=i;
for i:=m downto 1 do
begin
g[i]:=g[i+1];
l:=get2(x[i],i);
r:=get2(y[i],i);
if l<>r then
g[i,l]:=r;
end;
readln(k);
for i:=1 to k do
begin
readln(xx,yy);
fg:=f[xx-1];
for j:=1 to n do
if get3(fg[j])<>get2(g[yy+1,j],yy+1) then
fg[get3(fg[j])]:=get3(g[yy+1,j]);
ans:=0;
for j:=1 to n do bz[j]:=false;
for j:=1 to n do
begin
if bz[get3(fg[j])]=false then
begin
bz[get3(fg[j])]:=true;
inc(ans);
end;
end;
writeln(ans);
end;
close(input);
close(output);
end.