幸いこれもあまり迷わなかった。
https://yukicoder.me/problems/no/3602
問題
H*Wのグリッドがあり、各マスに整数値が書かれている。
以下のクエリに答えよ。
整数値Xが与えられる。
任意のマスにクイーンを置き、そこから最大2HW-1回まで動かす。
クイーンの置かれたマスに書かれた整数値のxorがXとなるようにできるか。
できるなら一例を示せ。
解法
まず元のH*W個の整数値をbit vectorとみなし、基底ベクトルを求めれば、それらのXORでXが構成できるかはすぐわかる。
あとは、1回ずつたどりたいマスがわかった場合の移動の仕方は以下。
- たどりたいマスのある行だけを考える。
- 直前の行で(r',c)にいた場合、とりあえず真下に移動して、(r,c)に到達する。
- 同じr行のたどりたいマスを通る。もし(r,c)はたどりたくない場合、最後にもう一度(r,c)に戻る。
- その後、次の行に移る。
int H,W,Q; ll A[20][20]; const int MAT=400; ll ma[MAT][MAT],pat[MAT][MAT]; ll V[404]; vector<int> cand[20]; // bitsetであるAを独立bit vectorにする際、結果をBに示す template<typename C> int gf2_rank(C A[MAT][MAT],C B[MAT][MAT],int H,int W) { /* input */ int i,j,k; FOR(i,H) FOR(j,H) B[i][j]=(i==j); FOR(i,H) { int be=i,mi=W+1; for(j=i;j<H;j++) { FOR(k,W) if(A[j][k]) break; if(k<mi) be=j,mi=k; } if(mi>=W) break; FOR(j,W) swap(A[i][j],A[be][j]); FOR(j,H) swap(B[i][j],B[be][j]); FOR(j,H) if(i!=j&&A[j][mi]) { FOR(k,W) A[j][k] ^= A[i][k]; FOR(k,H) B[j][k] ^= B[i][k]; } } return i; } void solve() { int i,j,k,l,r,x,y; string s; cin>>H>>W; FOR(y,H) { FOR(x,W) { cin>>A[y][x]; FOR(i,60) if(A[y][x]&(1LL<<i)) ma[y*W+x][i]=1; } } int rank=gf2_rank(ma,pat,H*W,60); FOR(i,rank) { FOR(j,60) if(ma[i][j]) V[i]|=1LL<<j; } cin>>Q; while(Q--) { ll X; cin>>X; if(X==0) { cout<<3<<endl; cout<<"1 1"<<endl; cout<<"1 2"<<endl; cout<<"1 1"<<endl; cout<<"1 2"<<endl; continue; } int B[404]={}; FOR(i,rank) { if(X&V[i]) { X^=V[i]; FOR(j,H*W) B[j]^=pat[i][j]; } } if(X) { cout<<-1<<endl; continue; } int pre=-1; vector<pair<int,int>> ret; FOR(y,H) { cand[y].clear(); FOR(x,W) if(B[y*W+x]) cand[y].push_back(x); if(cand[y].empty()) continue; if(pre!=-1) { if(count(ALL(cand[y]),pre)) { cand[y].erase(remove(ALL(cand[y]), pre), cand[y].end()); cand[y].insert(cand[y].begin(),pre); } else { cand[y].insert(cand[y].begin(),pre); cand[y].push_back(pre); } } pre=cand[y].back(); FORR(a,cand[y]) ret.push_back({y,a}); } cout<<ret.size()-1<<endl; FORR2(y,x,ret) cout<<y+1<<" "<<x+1<<endl; } }
まとめ
やることは難しくないけど、ちょっと実装に手間取った。