Loading... # ACWing 1291 轻拍牛头 题解 ## 题目描述 今天是贝茜的生日,为了庆祝自己的生日,贝茜邀你来玩一个游戏. 贝茜让 $N$ 头奶牛(编号 $1$ 到 $N$)坐成一个圈。 除了 $1$ 号与 $N$ 号奶牛外,$i$ 号奶牛与 $i-1$ 号和 $i+1$ 号奶牛相邻,$N$ 号奶牛与 $1$ 号奶牛相邻。 农夫约翰用很多纸条装满了一个桶,每一张纸条中包含一个 $1$ 到 $1000000$ 之间的数字。 接着每一头奶牛 $i$ 从桶中取出一张纸条,纸条上的数字用 $A_i$ 表示。 所有奶牛都选取完毕后,每头奶牛轮流走上一圈,当走到一头奶牛身旁时,如果自己手中的数字能够被该奶牛手中的数字整除,则拍打该牛的头。 牛们希望你帮助他们确定,每一头奶牛需要拍打的牛的数量。 即共有 $N$ 个整数 $A_1,A_2,…,A_N$,对于每一个数 $A_i$,求其他的数中有多少个是它的约数。 ## 输入格式 第一行包含整数 $N$。 接下来 $N$ 行,每行包含一个整数 $A_i$。 ## 输出格式 共 $N$ 行,第 $i$ 行的数字为第 $i$ 头牛需要拍打的牛的数量。 ## 解题思路 首先考虑朴素做法, 其时间复杂度为$O(N^2)$, 显然会超时, 不可取 考虑到$1\sim N$中所有数的约数个数之和不会超过$O(Nlog N)$, 可以尝试求出所有数 的约数, 但是求约数的时间复杂度为$O(\sqrt{N})$, 因此也会超时 这样我们不难发现一个事实:对于一个正整数$n$而言,进行质因数分解所需要的时间复杂度很高, 但是求他的倍数$kn$的时间复杂度很低,因此可以考虑计算$i$的所有倍数,不妨令`cnt[i]`表示$i$出现的次数, `ans[i]`表示拿到数字为$i$的奶牛,在$N$头奶牛拿到所有数字中约数的个数,则可以通过如下方式更新 - 首先预处理所有的数字,令`cnt[i]`表示数字`i`出现的次数 - 令$i$从$1$到$N$开始遍历, $j$为$i$的倍数。则应有: - 如果`j == i`则`ans[i] += cnt[j] - 1` - 否则`ans[i] += cnt[j]` ## AC代码 ```cpp #include <cstdio> #include <cstdlib> #include <iostream> #include <vector> #include <cstring> #include <map> #define endl '\n' using namespace std; const int N = 1e6 + 10; int n; int a[N], cnt[N], ans[N]; int main(){ ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); cin >> n; for(int i = 1; i <= n; i ++) { cin >> a[i]; cnt[a[i]] ++; } for(int i = 1; i <= N; i ++) { for(int j = i; j <= N; j += i) { if(j == i) ans[j] += cnt[i] - 1; else ans[j] += cnt[i]; } } for(int i = 1; i <= n; i ++) cout << ans[a[i]] << endl; return 0; } ``` 最后修改:2025 年 05 月 13 日 © 允许规范转载 赞 如果觉得我的文章对你有用,请随意赞赏