kmjp's blog

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

AtCoder AGC #006 : F - Blackout

これはコードは短いのよね。
http://agc006.contest.atcoder.jp/tasks/agc006_f

問題

2次元のグリッドがある。
初期状態ですべてのセルは白である。

ここで、M個のマス(A[i],B[i])を黒く塗ったとする。
以後、以下の手順で黒く塗るマスを増やしていく。

  • マス(x,y),(y,z)がともに黒で、(z,x)がまだ白なら(z,x)を黒く塗る

最終的に黒く塗られたマスはいくつになるか。

解法

上記手順を無向グラフとして考える。
色を塗る処理について、x→y、y→zの辺がある場合、z→xにも辺を追加する、と読み替えよう。
明らかにもともと弱連結成分でない頂点間に辺が張られることはないので、以下弱連結成分同士のみ考える。

各頂点をABC3色で塗り分けることを考えよう。
各辺において、A→B、B→C、C→Aのいずれかの対応関係を持つように塗っていく。
これは適当に1頂点の色を決めれば他の頂点も再帰的に決まる。
塗った後の状態について、以下の通り辺を追加できる。

  • A,B,Cの3色が登場する
    • すべての辺がA→B、B→C、C→Aのいずれかの対応をとる塗り方がある
      • A,B,Cに塗られた各頂点対に対しA→B、B→C、C→Aとなる辺をすべて追加することができる
    • 上記の塗り方がない(矛盾があり上記以外の構成(B→A、C→B、A→C)がどうしてもできてしまう)
      • この場合、全頂点間に双方向の辺を張れる。
  • 3色登場しない
    • 辺はこれ以上増やせない
int L[101010];
vector<pair<int,int>> E[101010];
int N,M;
ll NL[3],NG,NE;

void dfs(int cur,int pre) {
	NL[L[cur]]++;
	FORR(r,E[cur]) {
		if(r.second==1) NE++;
		if(L[r.first]==-1) {
			L[r.first]=(L[cur]+r.second)%3;
			dfs(r.first,cur);
		}
		else {
			if(L[r.first]!=(L[cur]+r.second)%3) NG++;
		}
	}
	
}

void solve() {
	int i,j,k,l,r,x,y; string s;
	
	cin>>N>>M;
	FOR(i,M) {
		cin>>x>>y;
		E[x-1].push_back({y-1,1});
		E[y-1].push_back({x-1,2});
	}
	
	MINUS(L);
	ll ret=0;
	FOR(i,N) if(L[i]==-1) {
		NL[0]=NL[1]=NL[2]=0;
		NG=NE=0;
		L[i]=0;
		dfs(i,-1);
		if(NG) ret += (NL[0]+NL[1]+NL[2])*(NL[0]+NL[1]+NL[2]);
		else if(NL[1]==0 || NL[2]==0) ret += NE;
		else ret += NL[0]*NL[1]+NL[1]*NL[2]+NL[2]*NL[0];
	}
	cout<<ret<<endl;
}

まとめ

いろいろ書いてあるけど、mod3で考えるとよいということね。