问题描述
原题来自:USACO 2008 Dec. Silver
今天是贝茜的生日,为了庆祝自己的生日,贝茜邀你来玩一个游戏。
贝茜让 N头奶牛坐成一个圈。除了 1 号与 N 号奶牛外,i 号奶牛与 i−1号和 i+1 号奶牛相邻,N 号奶牛与 1号奶牛相邻。农夫约翰用很多纸条装满了一个桶,每一张包含了一个 1 到 10 6的数字。
接着每一头奶牛 i 从桶中取出一张纸条 Ai ,每头奶牛轮流走一圈,同时拍打所有「编号是 Ai 的约数」的牛,然后走回到原来的位置。牛们希望你帮助他们确定,每一头奶牛需要拍打的牛。
输入
第一行包含一个整数 N;
接下来第二到第 N+1 行每行包含一个整数 Ai 。
输出
第一到第 N 行,第 i 行的输出表示第 i 头奶牛要拍打的牛数量。
样例输入
5
2
1
2
3
4
样例输出
2
0
2
1
3
提示
数据范围与提示:
对于全部数据,1≤N≤105 。
问题分析
看完题目第一个想法是枚举的n2,之后发现完全可以用桶来记元素,然后通过类似于筛法求素数的方法,求出某一个元素的倍数的个数,-1即为答案。
算法设计
先读入统计一遍所有的数,之后因为每个数只会被比他小的因数除去,所以枚举每个数的倍数。最好把一样的数用数组保存,不然可能一个个筛会超时。然后就是要注意j要从i而不是2i开始枚举,因为可能存在相同的数字,最后给ans减1就好了。
代码
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=1e6+10;
int n,a[N],cnt[N],ans[N],maxn;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
maxn=max(maxn,a[i]);
cnt[a[i]]++;
}
for(int i=1;i<=maxn;i++)
{
if(cnt[i])
{
for(int j=i;j<=maxn;j+=i)
ans[j]+=cnt[i];
}
}
for(int i=1;i<=n;i++)
printf("%d\n",ans[a[i]]-1);
return 0;
}
算法复杂度分析
使用枚举法的算法复杂度为O(n2);利用上述的筛选法,并且将相同的数字合并能够到一个数组中一起计算,算法复杂度下降到O(nloga)。