bzoj1015:[JSOI2008]星球大战starwar【并查集】
直接按照读入的顺序处理会很不方便,所以我们可以采用离线的方式,先把所有的输入读进来,然后倒过来处理最后一起输出就可以了。
至于具体的方法,从最后倒过来,记录到最后剩下的星球,然后按照最后的样子加入边之类的,用并查集处理联通块数量,然后原本摧毁了哪个星球就按相反的顺序加入星球和对应的边。
代码:
var f,s,a,num,v,next,ans:array[0..400001]of longint; b:array[0..400000]of boolean; n,m,i,j,k,t,p,q:longint; function getfather(i:longint):longint; begin if f[i]=-1 then exit(i) else f[i]:=getfather(f[i]); exit(f[i]); end; procedure union(p,q:longint); var x,y:longint; begin x:=getfather(p); y:=getfather(q); if x=y then exit; dec(t); if num[x]<=num[y] then begin f[y]:=x; num[x]:=num[x]+num[y]; end else begin f[x]:=y; num[y]:=num[x]+num[y]; end; end; procedure adde(uu,vv:longint); begin inc(k); v[k]:=vv; next[k]:=a[uu]; a[uu]:=k; inc(k); v[k]:=uu; next[k]:=a[vv]; a[vv]:=k; end; begin read(n,m); fillchar(f,sizeof(f),255); for i:=1 to m do begin read(p,q); adde(p,q); end; read(k); fillchar(b,sizeof(b),true); for i:=1 to k do begin read(s[i]); b[s[i]]:=false; end; t:=n-k; for i:=0 to n-1 do if b[i] then begin j:=a[i]; while j<>0 do begin if b[v[j]] then union(i,v[j]); j:=next[j]; end; ans[k+1]:=t; end; for i:=k downto 1 do begin j:=a[s[i]]; inc(t); while j<>0 do begin if b[v[j]] then union(s[i],v[j]); j:=next[j]; end; ans[i]:=t; b[s[i]]:=true; end; for i:=1 to k+1 do writeln(ans[i]); end.
发表回复