// ------------------------------------------------------------ // Project Euler 572 - Idempotent 3x3 integer matrices // Count matrices M with entries in [-LIM, LIM] such that M^2 = M. // ------------------------------------------------------------
// --------- Global parameters ---------- typedeflonglong ll; int LIM = 200; ll LIM2; // LIM^2
// Cache for factor pairs of t (u,v) with u*v=t, u,v in [-LIM,LIM] unordered_map<ll, vector<pair<int, int>>> div_cache;
// ---------- helpers ---------- boolok(ll x){ return -LIM <= x && x <= LIM; }
// All integer pairs (u,v) with u*v=t, u,v in [-LIM, LIM] const vector<pair<int, int>> &factorPairs(ll t) { auto it = div_cache.find(t); if (it != div_cache.end()) return it->second;
vector<pair<int, int>> res;
if (t == 0) { // (u,0) for all u, and (0,v) for all v != 0, include (0,0) once res.reserve(2 * (2 * LIM + 1) - 1); for (int u = -LIM; u <= LIM; ++u) res.push_back({u, 0}); for (int v = -LIM; v <= LIM; ++v) if (v != 0) res.push_back({0, v}); div_cache.emplace(t, std::move(res)); return div_cache.find(t)->second; }
ll at = abs(t); // If |t| > LIM^2, no (u,v) within bounds can satisfy u*v=t if (at > 1LL * LIM * LIM) { div_cache.emplace(t, std::move(res)); return div_cache.find(t)->second; }
ll rt = (ll)sqrt((longdouble)at); for (ll d = 1; d <= rt; ++d) { if (at % d) continue; ll q = at / d;
// generate signed pairs (u,v) such that u*v=t // and also include swapped (v,u) auto addPair = [&](ll u, ll v) { if (ok(u) && ok(v)) { res.push_back({(int)u, (int)v}); } };
// Precompute TwoZeroCount for given LIM: // counts solutions for the (canonical) diagonal pattern with exactly two zero multipliers, // which always ends up with bd=cg=fh=0 and two of x,y,z are 0. // The final count only depends on LIM, not on the specific diagonal permutation.
ll TWO_ZERO_COUNT = 0;
// -------- Case 0: x,y,z all nonzero -------- ll countCommon(ll bd, ll cg, ll fh, ll x, ll y, ll z) { // Consistency conditions (necessary and sufficient) // If any fails -> no solutions. // Use 128-bit for safety (though values are small for LIM=200). ll BD = bd, CG = cg, FH = fh, X = x, Y = y, Z = z;
if (FH * Z * Z != BD * CG || BD * X * X != FH * CG || CG * Y * Y != BD * FH) return0;
for (constauto &p1 : BDPairs) { ll b = p1.first; if (b == 0) continue; // bd != 0 in this case for (constauto &p2 : FHPairs) { ll f = p2.first; if (f == 0) continue; // fh != 0 in this case ll denom = b * f; if (mul % denom != 0) continue; ll g = mul / denom; if (!ok(g) || g == 0 || cg % g != 0) continue; ll c = cg / g; if (!ok(c) || c == 0) continue;
// -------- Case 1: exactly one of x,y,z is zero -------- // We normalize by permuting diagonal so that z=0 (i.e. e+i=1). ll countOneZero_Z0(int a, int e, int i) { // Here z=0, and x,y !=0 ll x = 1LL - a - e, y = 1LL - a - i, z = 1LL - e - i; if (z != 0 || x == 0 || y == 0) return0;
ll A = 1LL * a - 1LL * a * a, E = 1LL * e - 1LL * e * e, I = 1LL * i - 1LL * i * i;
if (((A + E + I) & 1) != 0) return0; ll bd = (A + E - I) / 2, cg = (A + I - E) / 2, fh = (E + I - A) / 2;
// In this situation, we must have bd=cg=0, otherwise contradictions arise. if (bd != 0 || cg != 0) return0;
// fh must be representable as product of two bounded ints constauto &FHPairs = factorPairs(fh); if (FHPairs.empty()) return0;
ll ans = 0;
// Family A: b=0, c=0. Variables (f,h) factor of fh, then choose d, g is determined. // We can count d without looping by reducing divisibility. for (constauto &pr : FHPairs) { ll f = pr.first; ll h = pr.second; if (f == 0 || h == 0) continue; // fh != 0 in this case
// Need d in [-LIM,LIM] such that g = d*x/f is integer and within [-LIM,LIM]. // Let g0 = gcd(|f|,|x|), f = g0*f1, x = g0*x1 (gcd(f1,x1)=1). // Then d must be multiple of |f1|, write d = f1*t, and g = t*x1. ll af = abs(f); ll ax = abs(x); ll g0 = __gcd(af, ax); ll f1 = af / g0; ll x1 = ax / g0;
ll T = min((ll)(LIM / f1), (ll)(LIM / x1)); // t in [-T..T] => count = 2T+1 ans += (2 * T + 1);
// Family B: d=0, g=0, but b != 0. // Need c = b*f / y integer and |c|<=LIM. // Reduce: let g1=gcd(|f|,|y|), f=g1*f2, y=g1*y2 (gcd(f2,y2)=1) // then b must be multiple of |y2|, b=y2*t (t!=0), // and c=t*f2, so |t| <= min(LIM/|y2|, LIM/|f2|). ll ay = abs(y); ll g1 = __gcd(af, ay); ll f2 = af / g1; ll y2 = ay / g1;
// Main per-diagonal counter ll countDiagonal(int a, int e, int i) { ll x = 1LL - a - e, y = 1LL - a - i, z = 1LL - e - i;
int zero = int(x == 0) + int(y == 0) + int(z == 0);
ll A = 1LL * a - 1LL * a * a, E = 1LL * e - 1LL * e * e, I = 1LL * i - 1LL * i * i;
if (((A + E + I) & 1) != 0) return0; ll bd = (A + E - I) / 2, cg = (A + I - E) / 2, fh = (E + I - A) / 2;
if (zero == 0) { // bd,cg,fh must be representable within bounds if (abs(bd) > LIM2 || abs(cg) > LIM2 || abs(fh) > LIM2) return0; returncountCommon(bd, cg, fh, x, y, z); } elseif (zero == 1) { // normalize to z=0 by diagonal permutation (counts are invariant under permutation conjugation) if (z == 0) { returncountOneZero_Z0(a, e, i); } elseif (x == 0) { // (a,e,i) -> (i,a,e) makes z' = a+e? actually e'+i' = a+e = 1 returncountOneZero_Z0(i, a, e); } else { // y==0: (a,e,i) -> (e,a,i) makes e'+i' = a+i = 1 returncountOneZero_Z0(e, a, i); } } elseif (zero == 2) { // Only possible (for trace 1 or 2) on the special 0/1 diagonals, and then bd=cg=fh=0. if (bd != 0 || cg != 0 || fh != 0) return0; return TWO_ZERO_COUNT; } else { // x=y=z=0 impossible for integer diagonal with trace 1 or 2 return0; } }
intmain() { LIM2 = 1LL * LIM * LIM; // Let H = sum_{k=1..LIM} floor(LIM/k) ll H = 0; for (int k = 1; k <= LIM; k++) H += LIM / k; // Derived closed-form: // TwoZeroCount = 8*H + 8*LIM*(LIM+1) + 1 // (works for x=±1 canonical, but independent of sign) TWO_ZERO_COUNT = 8LL * H + 8LL * LIM * (LIM + 1) + 1;
ll total = 1; if (LIM >= 1) ++total; // Only trace 1 or 2 are possible for non-zero idempotent 3x3 matrices for (int tr = 1; tr <= 2; ++tr) { for (int a = -LIM; a <= LIM; ++a) { for (int e = -LIM; e <= LIM; ++e) { int i = tr - a - e; if (i < -LIM || i > LIM) continue; total += countDiagonal(a, e, i); } } } cout << total << "\n"; }