1 条题解

  • 0
    @ 2026-10-26 20:16:52

    思路与算法

    枚举本原整数方向(x,y),gcd=1,横竖单列,两种斜率对称乘2。一条最大格点线含L点,所选相邻点的索引差至少g,其中g是满足g²(x²+y²)≥D²的最小正整数。在线上选N点方案为C(L-(g-1)(N-1),N)。S(k)=(W-(k-1)x+1)+(H-(k-1)y+1)+计k点连续段;最大线恰长L的数量为S(L)-2S(L+1)+S(L+2),据此累加。

    正确性说明

    任意至少2个共线点唯一决定本原方向及所在最大线。间距约束等价排序索引间隔至少g,移去各前缀额外空隙后得到无约束组合选取。长度L的最大线含max(L-k+1,0)个k段,二阶差分恰好提取指定L线数,因此每个点集仅计一次。

    复杂度

    Pascal表初始化O(500·50)。每组方向枚举O(WH),各方向长度循环总复杂度可写O(Σ方向 min(W/x,H/y)),粗上界O(WH·min(W,H)),实际由调和求和显著更小;另有求g的O(D)粗界。空间O(500·50)。所有20组满规模用例按原1秒限制执行。

    易错点

    模数1e9为合数,组合数用加法递推,不能用费马逆元。判断距离使用整数平方,避免开方边界。N=1时所有格点各是一种方案,单列;原网格有(W+1)(H+1)点。

    参考代码(C++11)

    #include <bits/stdc++.h>
    using namespace std;
    using ll = long long;
    const ll P = 1000000000;
    ll C[503][51];
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
        for (int i = 0; i <= 502; i++) {
            C[i][0] = 1;
            for (int j = 1; j <= 50 && j <= i; j++)
                C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % P;
        }
        int T;
        cin >> T;
        while (T--) {
            int N, W, H, D;
            cin >> N >> W >> H >> D;
            if (N == 1) {
                cout << (W + 1LL) * (H + 1) % P << '\n';
                continue;
            }
            ll ans = 0;
            auto process = [&](int x, int y, int mult) {
                int L = min(x ? W / x : 1000000, y ? H / y : 1000000) + 1;
                if (L < N)
                    return;
                int step = 1;
                while (1LL * step * step * (x * x + y * y) < 1LL * D * D)
                    step++;
                auto cnt = [&](int k) -> ll {
                    return max(0, W - (k - 1) * x + 1) * 1LL * max(0, H - (k - 1) * y + 1);
                };
                for (int k = N; k <= L; k++) {
                    int z = k - (step - 1) * (N - 1);
                    if (z < N)
                        continue;
                    ll exact = cnt(k) - 2 * cnt(k + 1) + cnt(k + 2);
                    ans = (ans + exact % P * C[z][N] % P * mult) % P;
                }
            };
            process(1, 0, 1);
            process(0, 1, 1);
            for (int x = 1; x <= W / (N - 1); x++)
                for (int y = 1; y <= H / (N - 1); y++)
                    if (__gcd(x, y) == 1)
                        process(x, y, 2);
            cout << ans << '\n';
        }
    }
    
    • 1

    信息

    ID
    1862
    时间
    1000ms
    内存
    128MiB
    难度
    普及+
    标签
    递交数
    0
    已通过
    0
    上传者