题意:a*b=S,给S 和一条边的最小长度,求多少种组合
有一个矩形的毯子,已知它一定不是正方形,并且已知最短边和毯子的面积,求这个毯子可能有多少种形状。
分析:首先,如果最短的边大于等于面积的平方根,两个大于面积平方根的边不可能组成面积等于给定面积的矩形。
所以这种情况是0. 然后最短边的范围就变成了1-1e6,问题也变成了在最短边(min_side)到面积(area)这些数中有多少对不同的数相乘等于面积(area)。可以转变为1-area中不同的数相乘为area的对数,再减去1-min_side-1中不同的数相乘为area的对数。然后利用算术基本定理中的(1)算出1-area中的正因数的个数,正因数关于sqrt(area)对称,每对称的两个相乘是area,因此只要因数的个数除以二就行啦,向下取整,正好也不会算上平方根。再就是1-min_side-1中的对数,暴力就行啦。(下图是转的)
相关知识:
1.有多少个约数:
先分解质因数
因数的次数分别是4,2,1所以约数的个数为(4+1)*(2+1)*(1+1)=5*3*2=30个
eg:
先分解质因数
720=24*32*51
因数的次数分别是4,2,1
所以约数的个数为(4+1)*(2+1)*(1+1)=5*3*2=30个
2.所有约数之和:
2004的约数之和为:1, 2, 3, 4, 6, 12, 167, 334, 501, 668, 1002 ,2004 = 4704
如何求一个数所有约数之和呢?
首先,应用算术基本定理,化简为素数方幂的乘积。
X = a1^k1 * a2^k2........an^kn
X的所有素数之和可用公式(1+a1 + a1^2...a1^k1) * (1+a2 + a2^2...a2^k2) * .....(1+an + an^2...an^kn)表示
如:
2004 = 2^2 * 3 *167
2004所有因子之和为(1 + 2 + 2^2) * (1 + 3) * ( 1 + 167) = 4704;
程序实现的时候,可利用等比数列快速求1 + a1 + a1^2 + .....a1^n;
思路:
求出它的每个质因子的个数,然后用公式求出它的约数个数。如果b * b > a,那么值一定为0,其余部分可以枚举b,删除。但是我觉得枚举应该会挂掉,
但是竟然没有挂。。
代码:
#include<stdio.h>
#include<string.h>
const int MAXN=1000100;
bool vis[MAXN];
long long prime[MAXN/10];
int tot=0;
void getPrime()//ÇóËØÊý
{
for(long long i=2;i<MAXN;i++)
if(!vis[i])
{
prime[tot++]=i;
for(long long j=i*i;j<MAXN;j+=i) vis[j]=true;
}
}
int a[1000];//±£´æËØÒò×Ó
int b[1000];//±£´æËØÒò×ӵĸöÊý
int cnt;
void sbreak(long long n){//½øÐÐËØÒò×Ó·Ö½â
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
cnt=0;
for(int i=0;prime[i]*prime[i]<=n;i++){
if(n%prime[i]==0){
a[cnt]=prime[i];
while(n%prime[i]==0){
b[cnt]++;
n/=prime[i];
}
cnt++;
}
}
if(n!=1){
a[cnt]=n;
b[cnt++]=1;
}
}
int gcd(int a,int b) //Çó×î´ó¹«Ô¼Êý
{
return b?gcd(b,a%b):a;
}
int main(){
int T,ans,kase=0,flag;
long long n;
getPrime();
scanf("%d",&T);
while(T--){
flag=1;//±êÖ¾£¬ÅжÏnÊÇÕýÊý»¹ÊǸºÊý
scanf("%lld",&n);
if(n<0) n=-n,flag=0;
sbreak(n);
int t=b[0];
if(!flag){//Èç¹ûnÊÇÆæÊý
if(t%2==0){
while(t%2==0) t/=2;
}
for(int i=0;i<cnt;i++){//½«ËüµÄËØÒò×ӵĸöÊý»¯ÎªÆæÊý
if(b[i]%2==0){
while(b[i]%2==0) b[i]/=2;
}
t=gcd(t,b[i]);
}
}
else for(int i=0;i<cnt;i++) t=gcd(t,b[i]);
printf("Case %d: %d\n",++kase,t);
}
return 0;
}
法二
#include<bitset>
#include<map>
#include<vector>
#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<stack>
#include<queue>
#include<set>
#define inf 0x3f3f3f3f
#define mem(a,x) memset(a,x,sizeof(a))
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
inline ll in()
{
ll res=0;char c;
while((c=getchar())<'0' || c>'9');
while(c>='0' && c<='9')res=res*10+c-'0',c=getchar();
return res;
}
const int N=1000010;
int prime[79000];
bitset<N> vis;
int p;
void init()
{
for(int i=2;i<N;i++)
{
if(!vis[i])
{
prime[p++]=i;
for(int j=i+i;j<N;j+=i)
{
vis[j]=1;
}
}
}
}
int main()
{
int T=in();
int ii=1;
init();
while(T--)
{
ll area=in(),min_side=in();
if(min_side>=sqrt(area))
{
printf("Case %d: %d\n",ii++,0);
continue;
}
ll tmp=area;
int ans=1;
for(int i=0;i<p && 1LL*prime[i]*prime[i]<=area;i++)
{
int cnt=0;
while(area%prime[i] == 0)
{
area/=prime[i];
cnt++;
}
ans*=(cnt+1);
}
if(area!=1) ans<<=1;
ans>>=1; //因数的个数除以二就是组数,同时排除了平方根
for(int i=1;i<min_side;i++) if(tmp%i==0) ans--; //min_side此时是小于sqrt(area)的
printf("Case %d: %d\n",ii++,ans);
}
return 0;
}