kmjp's blog

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

CSAcademy Round #53 : D. Parallel Rectangles

この問題、考えたことあったけどO(N^2)解しか思いつかなかったんだよな。
https://csacademy.com/contest/round-53/task/parallel-rectangles/

問題

2次元座標上でN個の異なる格子点の座標が与えられる。
このうち4つを結んでできる長方形で、4辺が軸に平行なものは何通りか。

解法

平方分割の要領で解く。
各X座標において、何個点があるかで処理を分けよう。

  • X座標がaである点がある程度以上多い場合
    • 他のX座標bをすべて探索し、(a,y),(b,y)両方に点があるようなyの数cを求めよう。(a,b)の対に対しc中2個選ぶ選び方の分だけ長方形が作れる。
    • なお、X座標がbである点も多い場合(a,b)と(b,a)で二重カウントしないように注意。
  • X座標がaである点がある程度以上少ない場合
    • X座標がaである点のうち、y座標の対を数え上げよう。例えば(a,y1)(a,y2)があるならmap[(y1,y2)]++というように数え上げる。
    • 全X座標について処理が終わったとき、map[(y1,y2)]が2以上なら、map[(y1,y2)]個から2つのX座標(x1,x2)を選択することで、Y座標がy1,y2であるような4つの格子点を選ぶことができる。
int N;
vector<int> X[201010];
int C[101010];

void solve() {
	int i,j,k,l,r,x,y; string s;
	
	cin>>N;
	FOR(i,N) {
		cin>>x>>y;
		X[x-1].push_back(y-1);
	}
	FOR(i,100000) {
		if(X[i].size()==1) X[i].clear();
		sort(ALL(X[i]));
	}
	
	ll ret=0;
	int S=40;
	map<pair<int,int>,int> M;
	FOR(i,100000) {
		if(X[i].size()>S) {
			FORR(c,X[i]) C[c]++;
			FOR(j,100000) if(j!=i && X[j].size()>=2) {
				int cnt=0;
				FORR(c,X[j]) cnt+=C[c];
				ret+=1LL*cnt*(cnt-1)/2;
			}
			FORR(c,X[i]) C[c]--;
			X[i].clear();
		}
		else {
			FOR(y,X[i].size()) FOR(x,y) M[{X[i][x],X[i][y]}]++;
		}
	}
	
	FORR(r,M) ret+=1LL*r.second*(r.second-1)/2;
	cout<<ret<<endl;
	
}

まとめ

うーん。