嘘~ 正在从服务器偷取页面 . . .

洛谷P9148 除法题 题解


洛谷P9148 除法题 题解

题目链接:P9148 除法题

题意

给定大小为 \(n\) 的集合 \(a\),保证其中元素互不相同且均为正整数。

如果我们从中按顺序取出三个元素 \(a, b, c\),则共有 \(n \cdot (n-1) \cdot (n-2)\) 种不同的选择方案。

现在对于一种选择方案 \((a,b,c)\),定义其权值为 \(\Bigl\lfloor\dfrac{a}{b}\Bigr\rfloor\Bigl\lfloor\dfrac{a}{c}\Bigr\rfloor\Bigl\lfloor\dfrac{b}{c}\Bigr\rfloor\)

你需要对所有的选择方案计算权值的总和,你只需输出这个总和对 \(2^{32}\) 取模的结果。

注:\(\lfloor a\rfloor\) 表示不大于 \(a\) 的最大整数。如 \(\lfloor 2.4\rfloor=2\)\(\lfloor 5\rfloor=5\)

输入格式

第一行,一个正整数 \(n\),表示序列的长度。

第二行,\(n\) 个正整数 \(a_1, a_2, \ldots, a_n\),每个数描述了集合 \(a\) 的一个元素,这些数互不相同。

输出格式

输出一行一个整数,表示答案对 \(2^{32}\) 取模的结果。

数据范围

对于 \(100\%\) 的数据,\(1 \le n, a_i \le 5000\)

很容易想到去枚举 \(a,b\) ,但是仔细想想会发现 \(c\) 很不好枚举

相反,如果我们枚举 \(c\) ,考察它对哪些 \(a,b\) 产生了贡献,这却是能做的。

可以发现,因为值域 \(V\) 很小,所以 \(\left\lfloor \frac{V}{c}\right\rfloor\) 的值域则更为小。

并且对于一个 \(\left\lfloor \frac{a}{c}\right\rfloor\) ,它包含了 \([a_1, a_2]\) 内的所有整数,所有 \(a \in [a_1,a_2]\)\(\left\lfloor \frac{a}{c}\right\rfloor\) 是相等的

那么对于 \(\left\lfloor \frac{a}{c}\right\rfloor,\left\lfloor \frac{b}{c}\right\rfloor\) ,它包含了一个矩阵 \((a_1,b_1),(a_2,b_2)\) 内的所有点,这个统计很容易由二维差分做到。

分析一下时间复杂度。枚举 \(c\) 这一部分的时间复杂度为 \[ \sum_{i=1}^V \frac{V}{i} \times \frac{V}{i}=V^2 \times \sum_{i=1}^V \frac{1}{i^2} \] 这里需要一个小知识 \[ \sum_{i = 1}^{\infty} \frac{1}{i^2}=\frac{\pi^2}{6} \] 因此这部分的时间复杂度为 \(\mathcal{O}(V^2)\) 。枚举 \(a,b\) 计算答案的部分显然也是 \(\mathcal{O}(V^2)\)

总时间复杂度 \(\mathcal{O}(V^2)\)

代码:

#include <bits/stdc++.h>
using namespace std;
// #define int long long
// #define INF 0x3f3f3f3f3f3f3f3f
void up(int &x,int y) { x < y ? x = y : 0; }
void down(int &x,int y) { x > y ? x = y : 0; }
#define N ((int)(5e3 + 15))

const int m = 5000;
char vis[N]; int a[N],b[N][N];
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    int n; cin >> n;
    for(int i = 1; i <= n; i++) cin >> a[i], vis[a[i]] = true;
    for(int c = 1; c <= m; c++) if(vis[c])
    {
        for(int i = 1; i <= m; i++)
        {
            int a1 = c * i, a2 = min(c * (i + 1) - 1, m); if(a1 > a2) break;
            for(int j = 1; j <= m; j++)
            {
                int b1 = c * j, b2 = min(c * (j + 1) - 1, m); if(b1 > b2) break;
                up(a1, c + 1); up(b1, c + 1);
                b[a1][b1] += i * j; b[a1][b2 + 1] -= i * j;
                b[a2 + 1][b1] -= i * j; b[a2 + 1][b2 + 1] += i * j;
            }
        }
    }
    for(int i = 1; i <= m; i++)
        for(int j = 1; j <= m; j++) b[i][j] += b[i][j - 1];
    for(int i = 1; i <= m; i++)
        for(int j = 1; j <= m; j++) b[i][j] += b[i - 1][j];
    unsigned int res = 0;
    for(int i = 1; i <= m; i++)
        for(int j = 1; j <= m; j++)
            if(i > j && vis[i] && vis[j]) res += (i / j) * b[i][j];
    cout << res << '\n';
    return 0;
}

参考文献

[1] https://www.luogu.com.cn/blog/McHf/solution-p9148


文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录