洛谷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$ 这一部分的时间复杂度为
这里需要一个小知识
因此这部分的时间复杂度为 $\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;
}
参考文献: