首页 > 代码库 > poj1091:跳蚤【容斥原理】
poj1091:跳蚤【容斥原理】
题目大意:中文题就不翻译了
思路:假设跳蚤选择X1个第一张卡片,X2个第二张卡片。。。Xn个第n张卡片,Xn+1张写着m的卡片,那么就可以列出方程:a1*X1+a2*X2+…+an*Xn+m*X(n+1)=1
由于可以向左跳和向右跳,因此题目即问上述不定方程是否有解?答案以及它的证明可以在任何一本数论书中找到,它的充要条件是(a1,a2,a3。。。an,m)|1 即a1,a2,a3。。。an,m互质,这样题目就成为:有n+1个正整数,其中最大的数为m,问所有符合条件的序列中有多少是互质的。
组合数学很多都是正难则反易的,考虑问题的背面有多少最大公约数不为1的?先把m分解质因数,m的每一个因子可以看成一个集合,集合中的元素为最大公约数为这个因子的序列,这个问题的答案便是所有集合的并中集合的元素,用容斥就显然了
最后将总数m^n减去最大公约数不为1的个数,结果就是互质的个数了
顺便吐槽下,虽然容斥属于集合论里面的东西,但经常用来证明数论题目,最早学它好像就是用来推导欧拉函数的公式时用的
#include <cstdio>
#include <string>
#include <iostream>
#include <math.h>
#define ll __int64
using namespace std;
llfactor[100000],h=0,stack[100000],top=0,mt,nt,ret1;
ll quickpow(ll n,ll m)
{
ll ret=1;
while (m)
{
if ((m & 1))ret*=n;
n*=n;
m>>=1;
}
return ret;
}
void dfs(ll step,ll now,ll layer,ll num)
{
if (step==layer){ret1+=quickpow(mt/num,nt);return ;}
for(inti=now+1;i<=h-layer+step+1;i++)dfs(step+1,now+1,layer,num*factor[i]);
}
int main()
{
ll n;
scanf("%I64d%I64d",&nt,&mt);
n=mt;
while ((n & 1)==0){h=1;factor[h]=2;n>>=1;}
ll q=sqrt(n);
for(ll i=3;i<=q && n!=1;i+=2)
{
if (n % i==0)factor[++h]=i;
while(n%i==0)n=n/i;
}
if (n!=1)factor[++h]=n;
ll ans=0,flag=-1;
for(ll i=1;i<=h;i++)
{
flag*=-1;
top=ret1=0;
dfs(0,0,i,1);
ans+=ret1*flag;
}
printf("%I64d\n",(ll)quickpow(mt,nt)-ans);
return 0;
}
poj1091:跳蚤【容斥原理】