LightOJ - 1341 Aladdin and the Flying Carpet 唯一分解定理

题意: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;
}

 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值