1 条题解
-
0
题解
把时间按整数年份计,设行星 的角度为 。忽略朝向、只看“共线”,等价于要求角度模 相等,也就是
$$2t\Bigl(\frac1{t_i}-\frac1{t_j}\Bigr)\in\mathbb Z\qquad(\forall i,j\in S). $$令 ,则行星 在同一直线上等价于 。选择 中任意基准 ,满足条件的时间集合是所有
$$t=k\cdot\frac1{g_S},\qquad g_S=\gcd_{i\in S}(f_i-f_b) $$的整数倍,故每年发生 次(可能与其他行星重合)的“ 全体共线”事件。每个 是一条有理数,直接由若干分数的最大公约数得到。
在某一时刻,共线的行星形成若干条直线(按角度模 分组),每条直线贡献 。若一大群共线,则其中任意子集也“共线”,但不能重复计数。定义:
- :行星集合 ()全部共线的次数/年(不管是否有其他行星同线),有 。
- :恰好集合 在某条直线上、且没有其他行星落在该直线上的次数/年。
显然 ,因此 是对 的超集莫比乌斯反演。集合大小只有 ,用布尔格上的莫比乌斯变换即可:初始 ,对每一位 ,对所有不含该位的子集执行
exact[S]-=exact[S∪{i}]。时间复杂度 。最后答案为
是一个有理数 ,按题意输出 。整个过程保持分数形式(),用 / 在 范围内即可安全运算;转换到模意义时再乘逆元。
复杂度
子集枚举与变换 ,存储两个分数组(分子、分母)也是 。
参考代码
#include <bits/stdc++.h> using namespace std; using i128 = __int128_t; constexpr int MOD = 1000000007; struct Frac { i128 num = 0; // numerator i128 den = 1; // denominator (>0 unless num==0) }; i128 abs128(i128 x) { return x >= 0 ? x : -x; } i128 gcd128(i128 a, i128 b) { if (a < 0) a = -a; if (b < 0) b = -b; while (b) { i128 t = a % b; a = b; b = t; } return a; } i128 lcm128(i128 a, i128 b) { return a / gcd128(a, b) * b; } void normalize(Frac &f) { if (f.num == 0) { f.den = 1; return; } i128 g = gcd128(abs128(f.num), f.den); f.num /= g; f.den /= g; if (f.den < 0) { f.den = -f.den; f.num = -f.num; } } Frac subFrac(const Frac &a, const Frac &b) { i128 l = lcm128(a.den, b.den); i128 x = a.num * (l / a.den); i128 y = b.num * (l / b.den); Frac res{ x - y, l }; normalize(res); return res; } Frac gcdFrac(Frac a, Frac b) { if (a.num == 0) return b; if (b.num == 0) return a; i128 l = lcm128(a.den, b.den); i128 x = a.num * (l / a.den); i128 y = b.num * (l / b.den); i128 g = gcd128(abs128(x), abs128(y)); Frac res{ g, l }; normalize(res); return res; } long long modPow(long long a, long long e) { long long r = 1; while (e) { if (e & 1) r = (__int128)r * a % MOD; a = (__int128)a * a % MOD; e >>= 1; } return r; } long long fracToMod(const Frac &f) { long long num = ( (f.num % MOD) + MOD ) % MOD; long long den = ( (f.den % MOD) + MOD ) % MOD; return (__int128)num * modPow(den, MOD - 2) % MOD; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n; if (!(cin >> n)) return 0; vector<long long> t(n); for (int i = 0; i < n; ++i) cin >> t[i]; vector<long long> w(n + 1, 0); for (int i = 2; i <= n; ++i) cin >> w[i]; int N = 1 << n; vector<Frac> r(N); // all-align frequency (not exclusive) for (int mask = 0; mask < N; ++mask) { int sz = __builtin_popcount(mask); if (sz < 2) continue; int base = __builtin_ctz(mask); Frac g; g.num = 0; g.den = 1; for (int i = 0; i < n; ++i) { if (i == base || !(mask & (1 << i))) continue; i128 num = 2LL * llabs(t[i] - t[base]); i128 den = (i128)t[i] * (i128)t[base]; i128 gg = gcd128(num, den); num /= gg; den /= gg; Frac cur{ num, den }; g = gcdFrac(g, cur); } r[mask] = g; } // Möbius over supersets: exact = mobius(r) vector<Frac> exact = r; for (int bit = 0; bit < n; ++bit) { for (int mask = 0; mask < N; ++mask) { if ((mask & (1 << bit)) == 0) { exact[mask] = subFrac(exact[mask], exact[mask | (1 << bit)]); } } } long long ans = 0; for (int mask = 0; mask < N; ++mask) { int sz = __builtin_popcount(mask); if (sz < 2) continue; if (exact[mask].num == 0) continue; long long freqMod = fracToMod(exact[mask]); ans = (ans + freqMod * (w[sz] % MOD)) % MOD; } cout << ans << "\n"; return 0; }
- 1
信息
- ID
- 5726
- 时间
- 2000ms
- 内存
- 256MiB
- 难度
- 7
- 标签
- 递交数
- 4
- 已通过
- 1
- 上传者