kmjp's blog

競技プログラミング参加記です

Codeforces #884 : E. Great Grids

苦戦したけどどうにか解けた。
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;
	}
}

まとめ

最終的なコードはともかく、実験が結構めんどい。