kmjp's blog

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

Croc Champ 2013 - Final : D. Tennis Rackets

これは自力で解けた。
http://codeforces.com/contest/309/problem/D

問題

1辺の長さが(N+1)の正三角形があり、各辺1毎の間隔でN個ずつの穴がある。
これらの穴のうち、三角形の頂点に近いM個は利用できない。

各辺にある穴を1個ずつ選んだ時、その3点が鈍角三角形となるような選び方の数を答えよ。

解法

O(N^2)で愚直に処理すれば間に合う。
まず2次元座標上で、3角形の頂点が(0,0)、(N+1,0)、( (N+1)/2,(N+1)*√3/2)にあるとする。

下辺中の穴Aを(a,0)、左辺中の穴Bを(b/2,b*√3/2)としたとき、Aが鈍角になる場合を列挙する。
そのためにはABを結ぶ線をAを中心に90度回転し、右辺との交点を求める。
するとこの交点のX座標は x=\frac{2a^2-ab+3Nb+3b}{2a+2b}となる。右辺で有効な穴はX座標で(N+M+2)/2~(2N-M+1)/2の間に1/2ずつの間隔で配置されているので上記xより右側に何個あるかカウントしていく。

あとは図形の対称性を活かして定数倍の高速化をすればよい。

int N,M;

void solve() {
	int i,j,k,l,r; string s;
	
	ll a,b,x;
	
	cin>>N>>M;
	ll ret=0;
	for(a=M+1;a<=(N+1)/2;a++) {
		ll t=0;
		for(b=M+1;b<N+1-M && b<2*a;b++) {
			x=(2*a*a-a*b+3*N*b+3*b);
			l = N+1+M+1;
			r = 2*(N+1)-(M+1);
			if(x/(a+b)<l) t += r-l+1;
			else if(x/(a+b)<=r) t+=r-(x/(a+b));
		}
		ret += t*(2-(a==(N+1)/2 && N%2));
	}
	
	cout << ret*3 << endl;
}

まとめ

直線の交差判定に√3が出てきて面倒だったが、結果には√は出てこないのですっきり。