poj1091 跳蚤 解不定方程的应用

这题目真变态,真恶心,确实是跟解不定方程有关系,可是那根本就不是重点,还是看了别人的思路才知道要用到那么多知识点,真是个 变态的好题目,做一道题 要了解很多


1、最大公约数原理
设d = gcd(A1, A2, ..., An),则必然可以找到或正或负的整数Xi, 使
A1X1 + A2X2 + ... + AnXn = d



.2、A1X1 + A2X2 + ... + AnXn = 1有解的充要条件是d = gcd(A1, A2, ..., An) = 1


还有人说了容斥定理,这个我不太懂 贴一下别人的分析 很好:http://blog.sina.com.cn/s/blog_67e046d10100reqb.html 

http://blog.csdn.net/bobten2008/article/details/4473864


3、容斥原理:设集合S中至少具有P1、P2、…、Pm中的一个性质的元素个数是
|S1 ∪ S2 ∪ S3 ∪ ... ∪ Sm| = ∑|Si| - ∑|Si ∩ Sj| + (-1)^(m+1) |S1 ∩ S2 ∩ S3 ∩ ... ∩ Sm|


4、容斥原理化简式:|S| - |S1 ∪ S2 ∪ S3 ∪ ... ∪ Sm| = |S| ∏ (1 - 1/|Si|)


解题思路:将M因式分解:M = P1^K1 * P2^K2 + ... + Pr^Kr
则区间[1, M]内的整数是Pi的倍数的有:Pi, 2Pi, 3Pi, ..., r1Pi (这里的r1 = M / Pi)
而同理,是Pi.Pk的倍数有:Pi.Pk, 2Pi.Pk, ..., rPi.Pk (这里的r = M / Pi.Pk)
当前n个数中都含有P1的因数的情况数有r1^n = (M / P1)^n种,含有P2的因数情况数有r2^n = (M / P2)^n种,……
而总可能数为M^n种,故结果res = M^n - [(M / P1)^n + (M / P2)^n + ... + (M / Pn)^n] + ... + (-1)^(n+1) * [M / (P1*P2*P3*...*Pk)],由容斥原理化简式得到
res = M^n ∏ (1 - 1/Pi^n)


容斥定理推出来的易懂公式其实就是:  公因子不为1的个数 f = t(1) - t(2) + t(3) - ... + (-1) ^ (k - 1) t(k)

符合要求个数为 m ^ n - f



这是本题要用到的知识点,现在开始分析,假设卡片标号分别是a1,a2,a3...an,M跳蚤跳对应号的次数是 x1,x2......xn,xn+1,那么本题其实就是要满足一个 方程 a1*x1+a2*x2+....+an*xn+M*xn+1=1

然后对M进行分解,排除公因子不是1的情况


#include<iostream>
#include<cstdio>
#include<list>
#include<algorithm>
#include<cstring>
#include<string>
#include<queue>
#include<stack>
#include<map>
#include<vector>
#include<cmath>
#include<memory.h>
#include<set>

#define ll long long
#define LL __int64
#define eps 1e-8

//const ll INF=9999999999999;

#define inf 0xfffffff

using namespace std;

//vector<pair<int,int> > G;
//typedef pair<int,int> P;
//vector<pair<int,int>> ::iterator iter;
//
//map<ll,int>mp;
//map<ll,int>::iterator p;
//
//vector<int>G[30012];

int dive[112];
int cnt;

void divide(int m)
{
	cnt=0;
	for(int i=2;i<=sqrt(double(m));i++)
	{
		if(m%i==0)
		{
			cnt++;
			dive[cnt]=i;
			while(m%i==0)
				m/=i;
		}
	}
	if(m!=1)
		dive[++cnt]=m;
}

ll multiquick(ll a,ll b)
{
	ll ans=1;
	while(b)
	{
		if(b&1)
		{
			ans*=a;
			b--;
		}
		b>>=1;
		a=a*a;
	}
	return ans;
}

int a[112];
ll tmp;
int n,m;

void dfs(int id,int num,int comon)
{
	int temp;
	if(num==comon)
	{
		temp=m;
		for(int i=1;i<=comon;i++)
			temp/=a[i];
		tmp+=multiquick(temp,n);
		return ;
	}
	for(int i=id+1;i<=cnt;i++)
	{
		a[id+1]=dive[i];
		dfs(i,id+1,comon);
	}
}

int main(void)
{
	while(scanf("%d %d",&n,&m)==2)
	{
		ll ans=0;
		divide(m);
		for(int i=1;i<=cnt;i++)
		{
			tmp=0;
			dfs(0,0,i);
			if(i&1)
				ans+=tmp;
			else
				ans-=tmp;
		}
		ans=multiquick(m,n)-ans;
		printf("%lld\n",ans);
	}
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值