苦戦したけどどうにか解けた。
https://codeforces.com/contest/1844/problem/E
問題
N*Mのグリッドの各マスに、A,B,Cのいずれかの文字を入れることを考える。
この時、
- 隣接マスは異なる文字でなければならない。
- 2*2の領域には、A,B,Cが1回以上含まれなければならない。
いくつか2*2の領域において、「この2マスの文字は一致する」という条件が与えられる。
条件を満たす文字の入れ方が一致するか判定せよ。
解法
各2*2の領域は、左上マスと右下マスが同じか、右上マスと左下マスが同じか、どちらか片方が成り立たなければならない。
このパターンは2行ごとに同じであるか完全に入れ替わる。列についても同じ。
そこで、各列・行に0/1を割り当て、各2*2領域毎に、列と行の割り当て値のxorの0/1に応じ、2パターンのどちらを取るかを定めることを考える。
こうするとこれは(N+M-2)変数に0/1を割り当てる問題になるので、Union-Findで解ける。
int T; int H,W,K; int Y[2][4040],X[2][4040]; template<int um> class UF { public: vector<int> par,rank,cnt,G[um]; UF() {par=rank=vector<int>(um,0); cnt=vector<int>(um,1); for(int i=0;i<um;i++) par[i]=i;} void reinit(int num=um) {int i; FOR(i,num) rank[i]=0,cnt[i]=1,par[i]=i;} int operator[](int x) {return (par[x]==x)?(x):(par[x] = operator[](par[x]));} int count(int x) { return cnt[operator[](x)];} int operator()(int x,int y) { if((x=operator[](x))==(y=operator[](y))) return x; cnt[y]=cnt[x]=cnt[x]+cnt[y]; if(rank[x]>rank[y]) return par[x]=y; rank[x]+=rank[x]==rank[y]; return par[y]=x; } void dump(int num=um) { //グループ分けした配列を作る int i; FOR(i,num) G[i].clear(); FOR(i,num) G[operator[](i)].push_back(i); } }; UF<8020> uf; void solve() { int i,j,k,l,r,x,y; string s; cin>>T; while(T--) { cin>>H>>W>>K; uf.reinit((H+W)*2); FOR(i,K) { FOR(j,2) { cin>>Y[j][i]>>X[j][i]; Y[j][i]--; X[j][i]--; } if(X[0][i]<X[1][i]) { uf(Y[0][i],H+X[0][i]); uf(Y[0][i]+H+W,H+X[0][i]+H+W); } else { uf(Y[0][i],H+X[1][i]+H+W); uf(Y[0][i]+H+W,H+X[1][i]); } } FOR(i,H+W) if(uf[i]==uf[i+H+W]) break; if(i<H+W) cout<<"NO"<<endl; else cout<<"YES"<<endl; } }
まとめ
最終的なコードはともかく、実験が結構めんどい。