kmjp's blog

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

AtCoder ABC #176 : E - Bomber

えらくFが難易度高かった回。
https://atcoder.jp/contests/abc176/tasks/abc176_e

問題

グリッド上M個のマスに爆破対象が設置されている。
どこかのマス(爆破対象があってもよい)を1つ指定し、爆弾を置くと、同じ列または同じ行にある爆破対象を破壊できるとする。
最適な場所に爆弾を置く場合、最大何個破壊できるか。

解法

まず、各列と行にある対象の個数を数え上げよう。
すると、爆破すべき列は最大個数の対象のある列のいずれかで、爆破すべき行は最大個数の対象のある行のいずれかである。
もし列と行を定めたとき、そのマスに爆破対象があると、破壊できる数は(列の対象数)+(行の対象数)-1であり、対象がないと(列の対象数)+(行の対象数)となる。
そこで、マスに爆破対象がないような列・行を探したい。

これは最大個数の対象がある列・行を総当たりしてしまえばよい。
そのまま愚直に行うとTLEするケースがあるが、マスに爆破対象があるのは高々M個なので、最大(M+1)を総当たりするうちに(列の対象数)+(行の対象数)となるマスがあれば見つかるので、途中で打ち切ればよい。

下記のコードは少し違うアプローチ。
総当たりしている点は同じだが、更新の可能性がない列・行は速めに打ち切っている。

int H,W,M;
int R[303030];
int C[303030];
set<pair<int,int>> S;
pair<int,int> PR[303030],PC[303030];

void solve() {
	int i,j,k,l,r,x,y; string s;
	
	cin>>H>>W>>M;
	FOR(i,M) {
		cin>>y>>x;
		y--,x--;
		R[y]++;
		C[x]++;
		S.insert({y,x});
	}
	
	FOR(i,H) PR[i]={-R[i],i};
	FOR(i,W) PC[i]={-C[i],i};
	
	sort(PR,PR+H);
	sort(PC,PC+W);
	
	int ma=0;
	FOR(y,H) if(PR[y].first) {
		FOR(x,W) {
			int ty=-PR[y].first;
			int tx=-PC[x].first;
			if(ty+tx<=ma) break;
			if(tx==0) break;
			int cnt=ty+tx-(S.count({PR[y].second,PC[x].second}));
			ma=max(ma,cnt);
		}
	}
	
	cout<<ma<<endl;
	
}

まとめ

これ発音はボマーなのかボンバーなのか…元ネタを推測すると後者で良い?