エキシビジョンオープン参加で、なんとかオープン部門のA First Acceptedを取れた。
Bは時間切れ。
http://code-festival-2014-exhibition-open.contest.atcoder.jp/tasks/code_festival_exhibition_a
問題
N点M辺の無向辺グラフがある。
頂点iには、初期状態で頂点番号iが格納されている。
ここで、互いに辺で連結されている3つの頂点があったとき、3頂点に格納される番号を(a,b,c)とすると、(b,c,a)と番号をずらすことができる。これを回転と呼ぶ。
終了状態の頂点の番号Y[i]が与えられる。
回転を任意回数行い、初期状態から終了状態に遷移可能かどうか判定せよ。
解法
まず、初期状態と終了状態の頂点番号が回転で移動可能かどうかを示す。
これは回転できる頂点群をUnion-Findで連結していき、i番とY[i]番の頂点が連結しているか判定すればよい。
3頂点が互いに辺で結ばれているかどうかは、3頂点を総当たりするとO(N^3)で間に合わない。
しかし、2辺を列挙してその2辺が1つの点を共有し、かつ残りの2点を結ぶ別の辺があるかどうか、という判定にするとO(M^2)ですむ。
今回はMがNと同程度で小さいので、こちらで判定するとよい。
これでY[i]番の頂点番号をi番に持ってこれることは判定できたとして、15パズルのように最後2頂点の番号が逆になってしまう可能性がある。
頂点番号を(1,2,3)の状態から回転して得られる状態(2,3,1)、(3,1,2)はいずれも転置数が偶数である。
よって、上記逆転の有無を判定するには、各連結成分において転置数の偶奇を調べればよい。
class UF { public: static const int ufmax=100052; int ufpar[ufmax],ufrank[ufmax],ufcnt[ufmax]; UF() { init();} void init(){int i; FOR(i,ufmax) { ufpar[i]=i; ufrank[i]=0; ufcnt[i]=1; } } int find(int x) { return (ufpar[x]==x)?(x):(ufpar[x] = find(ufpar[x]));} int operator[](int x) {return find(x);} int count(int x) {return ufcnt[find(x)];} void unite(int x,int y) { x = find(x); y = find(y); if(x==y) return; if(ufrank[x]<ufrank[y]) ufpar[x]=y, ufcnt[y]+=ufcnt[x]; else {ufpar[y]=x; ufcnt[x]+=ufcnt[y]; if(ufrank[x]==ufrank[y]) ufrank[x]++;} } }; int N,M; int A[2001],B[2001]; int Y[2001]; int mat[2001][2001]; int ten[2001]; void solve() { int i,j,k,l,r,x,y; string s; cin>>N>>M; UF uf; FOR(i,M) { cin>>A[i]>>B[i]; A[i]--,B[i]--; mat[A[i]][B[i]]=mat[B[i]][A[i]]=1; } FOR(i,N) { cin>>Y[i]; Y[i]--; } FOR(x,M) for(y=x+1;y<M;y++) { int un=0; if(A[x]==A[y] && mat[B[x]][B[y]]) un++; if(A[x]==B[y] && mat[B[x]][A[y]]) un++; if(B[x]==A[y] && mat[A[x]][B[y]]) un++; if(B[x]==B[y] && mat[A[x]][A[y]]) un++; if(un) { uf.unite(A[x],A[y]); uf.unite(B[x],B[y]); uf.unite(A[x],B[x]); uf.unite(A[x],B[y]); uf.unite(A[y],B[x]); uf.unite(A[y],B[y]); } } FOR(i,N) if(uf[i]!=uf[Y[i]]) return _P("NO\n"); j=0; FOR(x,N) for(y=x+1;y<N;y++) if(uf[x]==uf[y] && Y[x]>Y[y]) ten[uf[x]]++; FOR(i,N) if(ten[i]%2) return _P("NO\n"); _P("YES\n"); }
まとめ
とっさに転置数の事を思い出してよかった。