さてD。だんだん難しくなってきた。
http://codeforces.com/contest/292/problem/D
問題
N個の点とM個の辺からなるグラフが与えられる。
そこにK個のクエリが与えられる。
クエリは2つの数値L,Rからなり、M辺のうちL~R番目のものを取り除いた場合に、グラフが何個の連結要素からなるかを答える。
解法
Union-Findデータ構造を辺の数×2だけ準備する。
まずは、辺が0の状態から1本目、2本目…と辺を増やした場合の点の連結状態をUnion-Findデータ構造で作る。
次に、逆方向に辺が0の状態からM本目、(M-1)本目…と辺を増やした場合のデータ構造を作る。
各クエリL,Rに対しては、1~(L-1)本目までの連結状態と(R+1)~M本目の連結状態をさらにUnionすればよい。
int N,M,K; int TF[10001][2]; class UF { public: static const int ufmax=502; int ufpar[ufmax],ufrank[ufmax]; void UF_init(){int i; FOR(i,ufmax) { ufpar[i]=i; ufrank[i]=0; } } int UF_find(int x) { return (ufpar[x]==x)?(x):(ufpar[x] = UF_find(ufpar[x]));} void UF_unite(int x,int y) { x = UF_find(x); y = UF_find(y); if(x==y) return; if(ufrank[x]<ufrank[y]) ufpar[x]=y; else {ufpar[y]=x; if(ufrank[x]==ufrank[y]) ufrank[x]++;} } }; UF uf[2][10002]; void solve() { int f,r,i,j,k,l,x,y; int ma=0; GET2(&N,&M); FOR(i,M) { TF[i][0]=GETi()-1; TF[i][1]=GETi()-1; } FOR(i,10001) { uf[0][i].UF_init(); uf[1][i].UF_init(); } FOR(i,M) { uf[0][i+1]=uf[0][i]; uf[0][i+1].UF_unite(TF[i][0],TF[i][1]); } for(i=M;i>=1;i--) { uf[1][i-1]=uf[1][i]; uf[1][i-1].UF_unite(TF[i-1][0],TF[i-1][1]); } K=GETi(); FOR(j,K) { int l=GETi()-1; int r=GETi()-1; UF tuf1,tuf2; tuf1=uf[0][l]; tuf2=uf[1][r+1]; FOR(i,N) tuf1.UF_unite(i,tuf2.UF_find(i)); int n=0; FOR(i,N) if(tuf1.UF_find(i)==i) n++; _P("%d\n",n); } return; }
まとめ
両側のUnion-Findを合わせりゃいいんじゃね?と考え付けたのは良かった。
残念なのは、手元のUnion-Findライブラリがクラスの形になっておらず1つしかデータを持てなかったため、辺追加毎に複数のデータ構造を持たせられる形になっていなかったこと。
慌ててクラス化したけど、それで若干時間食ったな…。