19:卡片(即POJ 1091)

Description


在一张卡片上面,有N+1个正整数,最后一个数是M,而前N个数都不超过M,设为a1,a2, a3, …, aN, 问是否存在整数x1,x2, x3, …, xN, x(N+1), 满足x1*a1 + x2*a2 +...+ xN*aN +x(N+1)*M = 1。
当确定NM后,显然一共有M^N张不同的卡片。现在的问题是,在这所有的卡片中,有多少张满足上面条件。


Input


多组数据。两个整数NM(N <= 10 , M <= 50), 最后的结果不超过2^63

Output


满足条件的卡片数。

SampleInput


2 4

SampleOutput


12

Hint


长整型请用long long


解题思路:(利用了容斥原理)
(1)题意:n+1个自然数,其中m是确定的,其他n个数都不超过m,有多少种方案使得a1x1+a2x2……+an+1xn+1=1.根据欧几里得,可知题目是要求有多少个这样的序列a1,a2……an+1,他们的最大公约数为1.由于其中一个数确定是m,而其他的数都小于等于m,所以我们只要把m分解质因数,如m=60,那么m分解得 m=2^2*3*5,利用容斥原理,总的可能数是m^n,m以内有2这个质因数的个数是m/2,所以序列包含2这个公因子的种类有(m/2)^n,依此类推……
(2)题意具体解题步骤如下:
1、求出满m的所有质因子,存入数组num
2、求出总的序列个数吗m^n
3、设t(k)表示数列最大公约数为(k个质因子乘积)的数列的个数

f=m^n-t(1)+t(2)-t(3)+..(-1)^k*t(k);
答案 = (m ^ n) - (有公因数2的n元组)- (有公因数3的n元组)- (有公因数5的n元组)+ (有公因数2,3的n元组) +(有公因数2,5的n元组) + (有公因数3,5的n元组)- (有公因数2,3,5的n元组)。这个比公式形象些
有公因数d的n元组,每个位置上有 (m/d)个选择(1 ~ m里面有m/d个d的倍数),根据乘法原理,可以得出有公因数d的n元组有 (m/d)^n 个。




package OJ;

import java.util.*;
import java.math.*;

public class P19_temp {

	static BigInteger one = BigInteger.ONE;
	static BigInteger zero = BigInteger.ZERO;
	static BigInteger per;
	static int n,m,total;
	static int[] s = new int[130000];
	static int[] num = new int[130000];//存储m的质因子
	
	static void getPrime(int m){ //求m的质因子
		int i,j = 0;
		total = 0;
		for(i=2; i*i<=m; i++){ //这个平方需要注意一下
			if(m%i==0){
				while(m%i==0)
					m = m/i;
				num[total] = i;
				total++;
			}
		}
		if(m!=1){
			num[total] = m;
			total++; //total表示实际质因子的个数
		}
	}
	
	static BigInteger power(int m,int n){ //求m^n
		BigInteger power = new BigInteger(""+m);
		BigInteger k = power;
		for(int i=1; i<n; i++){
			power = k.multiply(power);
		}
		return power;
	}
	
	static void get(int a, int b, int c){//a:在数组中的起始位置 b:含的个数 c:公共质因子个数; 
		if(b==c){
			int t = m;
			for(int i=0; i<c; i++){
				t = t/s[i];
			}//t表示每位上有几个包含质因子的数
			per = per.add(power(t, n));
		}
		else{//假如含有的质因子个数!=公共质因子数,s[b]入栈,递归;递归完成一次后,s[b]更新,递增一个由num[i]到num[i+1]
			for(int i=a; i<total; i++){
				s[b] = num[i];
				get(i+1,b+1,c);
			}
		}
	}
	
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		BigInteger res;
		Scanner in = new Scanner(System.in);
		n = in.nextInt();
		m = in.nextInt();
		getPrime(m);
		res = power(m, n);
		for(int i=0; i<total; i++){
			per = zero;
			get(0, 0, i+1);//包含i+1个质因子,有多少种组合
			if(i%2==0)
				res = res.subtract(per);//假如i+1为奇数,符号为负
			else
				res = res.add(per);//假如i+1为偶数,符号为正
		}
		System.out.println(res);
	}

}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值