约数也就是一个整数的所有整数因子
从小到达枚举所有的正整数,一个个判断是否是当前数的约数,当然枚举的时候可以进行优化,不用全部枚举,枚举一半的因子就能求出另一个因子了,可以将时间复杂度降低到O(sqrt(n))
代码:
#include
#include
#include
using namespace std;
set<int>ans; //由于最后要顺序输出并且不重复,因此需要保存一下
int n,a;
int main()
{
cin>>n;
while(n--)
{
cin>>a;
ans.clear();
for(int i=1;i<=a/i;i++)
{
if(a%i==0)
{
ans.insert(i);
ans.insert(a/i);
}
}
for(auto i : ans)
{
cout<<i<<" ";
}
cout<<endl;
}
return 0;
}
对于每个数,可以拆成
N = p1a1 * p2a2 * p3a3 … pkak
而对于N的某个约数,d = p1b1 * p2b2 * p3b3 … pkbk
N的每个约数由b1~bk的取值组合决定,只要b1,b2,b3…bk序列不一样,就对应不同的约数,因此这个序列的种类个数就是约束的个数,b1的取值范围为[0,a1] ,b2的取值范围为[0,a2],b3的取值范围为[0,a3],因此约束的个数最终为(a1+1) * (a2+1) * (a3+1)…(ak+1)
刷题链接:https://www.acwing.com/problem/content/description/872/
思路分析:咱也不能只能把全部的数乘在一起,然后进行因式分解,根本存不了那么大的数,对每个数进行单独的因式分解就行,因为质因子指数的取值序列反正是取决于每一个因子的。
AC代码:
#include
#include
using namespace std;
typedef long long LL;
const int p=1e9+7;
map<int,int>sushu;
int n,a;
int main()
{
cin>>n;
while(n--)
{
cin>>a;
for(int i=2;i<=a/i;i++) //对每个数分解质因子
{
while(a%i==0)
{
sushu[i]++;
a/=i;
}
}
if(a>1) sushu[a]+=1;
}
LL ans =1;
for(auto i:sushu)
{
ans=(ans%p*(i.second+1)%p)%p;
}
cout<<ans;
return 0;
}
对于每个数,可以拆成
N = p1a1 * p2a2 * p3a3 … pkak
所有约数的和为:
(p10+p11+…+p1a1) * (p20+p21+…+p2a2) * … (pk0+pk1+…+pkak)
证明:把括号展开后为()+()+…+()的形式,每一项是从上面的某几个括号选出来的一个,假设某一项从第一个括号中选出的是p1b1 ,从第二个括号选出的是p2b2,从第k个括号选出来的是pkbk ,因此展开后的每一项都是p1b1 * p2b2 * …* pkbk的形式,一共有多少项呢?实际就是从每个括号中选出的数的种类数相乘,也就是(a1+1) * (a2+1) * (a3+1)…(ak+1)项,每一项其实就对应N的一个约数,总和就是这个公式
#include
#include
#include
using namespace std;
typedef long long LL;
const int p=1e9+7;
map<int,int>sushu; //用hash表unordered_map来存会更快一些,查找时间为O(1),map内部是红黑树,查找时间是O(logn)
int n,a;
int main()
{
cin>>n;
while(n--)
{
cin>>a;
for(int i=2;i<=a/i;i++) //对每个数分解质因子,并将质因子的底数和指数保存下来
{
while(a%i==0)
{
sushu[i]++;
a/=i;
}
}
if(a>1) sushu[a]+=1;
}
LL ans =1;
for(auto i:sushu)
{
LL tmp=1,di=i.first;
for(int j=1;j<=i.second;j++)
{
tmp=(tmp+di)%p;
di=(di*i.first)%p; //实现p1^0,p1^1,p1^2.....不用pow比较慢,并且还可能超出数据范围
}
ans=(ans*tmp)%p;
}
cout<<ans;
return 0;
}
注意最后算和和乘积的时候要开long long,拿不准就全部都开long long
题目链接:https://www.acwing.com/problem/content/description/99/
分析思路:这是求约数和的变题,涉及到分解质因数,约数求和公式,分治,快速幂几种算法
首先对AB进行分解质因子:
A = p1a1 * p2a2 * p3a3 … pkak
AB = p1a1*B * p2a2*B * p3a3*B … pkak*B,也就是A的某个质因子有n个,其AB的相应质因子就有n*B个,就相当于将A重复B次
然后求约数之和:
公式:
(p10+p11+…+p1a1) * (p20+p21+…+p2a2) * … (pk0+pk1+…+pkak)
如果对每一项进行从0-a的线性求法,时间复杂度为O(n),会超时,可以采用分治的方法将时间复杂度降为O(logn),对于求(p10+p11+…+p1a1) ,可以将其分而治之,将其分为左右两半,这两半之间是有联系的,求出了左半部分,右半部分也就求出来了,因此只需要求左半部分,递归下去就只需要O(logn)的时间复杂度
举个栗子:
当最后一项指数为奇数,总共有偶数项,对半分即可
20+21+22+23+24+25
23+24+25 = 23 * (20+21+22)
总和 = 20+21+22+ 23 * (20+21+22)
当最后一项指数为偶数,总共有奇数项,最后一项单独算,前面的偶数项对半分
20+21+22+23+24
22+23 = 22 * (20+21)
总和 = 20+21+ 22 * (20+21)+ 24
最后,在求幂次的时候不能之间用pow()函数,因为要对p进行取余运算,pow得到的是double类型,不能之间对int型进行取余运算,另外由于要边乘边取余,之间pow后再取余得到的值可能不正确
AC代码:
标准的按照过程写法
#include
#include
#include
using namespace std;
typedef long long LL;
LL a,b;
const LL p=9901;
unordered_map<LL,LL>primes;
LL quick(LL x,LL y) //快速幂
{
LL ans=1,di=x;
while(y)
{
if(y%2) //y为奇数,二进制最低位为1,乘上当前底数
{
ans=(ans*di)%p;
}
di=(di*di)%p; //底数增加
y>>=1; //右移一位
}
return ans;
}
LL deal(int x,int y) //求x的0次方加到x的y次方
{
if(y==0) return 1;
LL le=(y-1)/2; //左半部分的结尾
LL l= deal(x,le); //只算出左半部分的值
LL r=(l*quick(x,le+1))%p; //得出右半部分的值
if(y%2) return (l+r)%p; //y是奇数表示有偶数项
else return (l+r+quick(x,y))%p; //y是偶数表示有奇数项,要多加上最后一个
}
int main()
{
cin>>a>>b;
if(a==0)
{
cout<<0; return 0;
}
for(LL i=2;i<=a/i;i++) //首先进行质因子分解
{
LL t=0;
while(a%i==0) //是约数也是质数
{
t++; a/=i;
}
primes[i]=t*b; //有多少个a相乘,质因子就有多少个
}
if(a>1) primes[a]=b; //相当于是1*b
LL ans=1;
for(auto i:primes)
{
cout<<i.first<<" "<<i.second<<endl;
LL tmp=deal(i.first,i.second);
ans=(ans*tmp)%p;
}
cout<<ans;
return 0;
}
代码化简式写法:(不需要将质因子存起来)
#include
#include
using namespace std;
typedef long long LL;
LL a,b;
const LL p=9901;
LL quick(LL x,LL y) //快速幂
{
LL ans=1,di=x;
while(y)
{
if(y%2) ans=(ans*di)%p;
di=(di*di)%p; //底数增加
y>>=1; //右移一位
}
return ans;
}
LL deal(int x,int y) //求x的0次方加到x的y次方
{
if(y==0) return 1;
LL le=(y-1)/2; //左半部分的结尾
LL l= deal(x,le); //只算出左半部分的值
LL r=(l*quick(x,le+1))%p; //得出右半部分的值
if(y%2) return (l+r)%p; //y是奇数表示有偶数项
else return (l+r+quick(x,y))%p; //y是偶数表示有奇数项,要多加上最后一个
}
int main()
{
cin>>a>>b;
if(a==0)
{
cout<<0; return 0;
}
LL ans=1;
for(LL i=2;i<=a/i;i++) //首先进行质因子分解
{
LL t=0;
while(a%i==0) //是约数也是质数
{
t++; a/=i;
}
ans =(ans*deal(i,t*b))%p ;
}
if(a>1) ans =(ans*deal(a,b))%p; //相当于是1*b
cout<<ans;
return 0;
}
完结~~~