1 条题解

  • 0
    @ 2025-12-1 23:08:16

    题解

    把时间按整数年份计,设行星 ii 的角度为 θi(t)=2πt/ti\theta_i(t)=2\pi t/t_i。忽略朝向、只看“共线”,等价于要求角度模 π\pi 相等,也就是

    $$2t\Bigl(\frac1{t_i}-\frac1{t_j}\Bigr)\in\mathbb Z\qquad(\forall i,j\in S). $$

    fi=2/tif_i=2/t_i,则行星 SS 在同一直线上等价于 fitfjt(mod1)f_i t\equiv f_j t\pmod 1。选择 SS 中任意基准 bb,满足条件的时间集合是所有

    $$t=k\cdot\frac1{g_S},\qquad g_S=\gcd_{i\in S}(f_i-f_b) $$

    的整数倍,故每年发生 gSg_S 次(可能与其他行星重合)的“SS 全体共线”事件。每个 gSg_S 是一条有理数,直接由若干分数的最大公约数得到。

    在某一时刻,共线的行星形成若干条直线(按角度模 π\pi 分组),每条直线贡献 wLw_{|L|}。若一大群共线,则其中任意子集也“共线”,但不能重复计数。定义:

    • rSr_S:行星集合 SSS2|S|\ge2)全部共线的次数/年(不管是否有其他行星同线),有 rS=gcdiS(2titb/(titb))r_S=\gcd_{i\in S}(2|t_i-t_b|/(t_it_b))
    • exactSexact_S:恰好集合 SS 在某条直线上、且没有其他行星落在该直线上的次数/年。

    显然 rS=TSexactTr_S=\sum_{T\supseteq S} exact_T,因此 exactexact 是对 rr 的超集莫比乌斯反演。集合大小只有 n20n\le20,用布尔格上的莫比乌斯变换即可:初始 exact=rexact=r,对每一位 ii,对所有不含该位的子集执行 exact[S]-=exact[S∪{i}]。时间复杂度 O(n2n)\mathcal O(n2^n)

    最后答案为

    S2exactSwS,\sum_{|S|\ge2} exact_S\cdot w_{|S|},

    是一个有理数 p/qp/q,按题意输出 pq1mod109+7p\cdot q^{-1}\bmod 10^9+7。整个过程保持分数形式(num/den\text{num}/\text{den}),用 lcm\mathrm{lcm} / gcd\gcd__int128\_\_int128 范围内即可安全运算;转换到模意义时再乘逆元。

    复杂度

    子集枚举与变换 O(n2n)\mathcal O(n2^n),存储两个分数组(分子、分母)也是 O(2n)\mathcal O(2^n)

    参考代码

    #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
    上传者