首页 > 代码库 > 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:跳蚤【容斥原理】