TopCoder SRM 327 Div1 Medium QuadraticEquations

問題

tについての方程式at^2 + bt + c = 0であって、
係数a, b, cが-k以上k以下の整数で、
t = (x + y * √d) / zを解の一つとして持つようなものはいくつあるか、求めよ。

制約条件

0≦k≦10^6
-1000≦x, y, z≦1000
zは0でない
1≦d≦1000

方針

dが平方数でない場合とそうでない場合で場合わけする。


dが平方数でないとき、tの解はt = (x ± y * √d) / zになる。

このとき、a, b, cはx, y, d, zの式で表せる。
a, b, cの公約数を1にしておいて、a, b, cのうち絶対値が最も小さいもの分だけ
方程式に定数倍の自由度があるので、それが答え。


dが平方数のとき、tの解はt = p / q(p, qはx, y, z, dから計算される値)と書ける。
もう一つの解をt = u / vと置く。
すると、方程式は
qut^2 - (pu + qv)t + pv = 0となる。


aについての制約から、
-k≦qu≦k ⇔ -k / q ≦ u ≦ k / q
なので、この範囲でuを一つずつループを回して決めうちする。


bについての制約から、
-k≦pv≦k
cについての制約から、
(-k + pu)/q ≦ v ≦ (k + pu)/q


がそれぞれ成り立つので、vに関する連立不等式となり、
可能なvの値の範囲がわかるので、このときの式の個数がわかる。
これを全ての可能なuについて足し合わせればいい。

ソースコード

// solve(a / c <= x <= b / c)
pi solve(ll a, ll b, ll c){
	if(c < 0) return solve(-b, -a, -c);
	if(c == 0){
		if(a <= 0 && b >= 0) return mp(-inf, inf);
		return mp(inf, -inf);
	}
	
	ll s = a / c - 2, t = b / c + 2;
	while(s * c < a) s++;
	while(t * c > b) t--;
	return mp(s, t);
}

class QuadraticEquations {
	public:
	long long howMany(int x, int y, int d, int z, int k) 
	{
		if(y == 0) d = 1;
		
		int sd = (int)sqrt(d + 1e-9);
		if(sd * sd == d){
			ll q = z, p = x + y * sd;
			ll g = __gcd(q, p);
			q /= g; p /= g;
			if(q < 0) q *= -1, p *= -1;
			
			ll ans = 0;
			for(int u = -k; u <= k; u++){
				
				if(q * u < -k || q * u > k) continue;
				
				pi a = solve(-k + p * u, k + p * u, q);
				pi b = solve(-k, k, p);
				
				ll lo = max(a.first, b.first), hi = min(a.second, b.second);
				if(lo <= hi) ans += hi - lo + 1;
			}
			return ans;
		}
		else{
			ll g = __gcd(__gcd((ll)z * z, 2ll * x * z), x * x - (ll)y * y * d);
			g = abs(g);
			
			ll mx = z * z / g;
			mx = max(mx, abs(x * x - (ll)y * y * d) / g);
			mx = max(mx, abs(2ll * x * z) / g);
			return k / mx * 2 + 1;
		}
	}
};