program1 n!%P(P为质数)

我们发现n! mod P的计算过程是以P为周期的的,举例如下:
n = 10, P = 3
n! = 1 * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10
    = 1 * 2 *
       4 * 5 *
       7 * 8 *
      10 *
      3 * 6 * 9
   = (1 * 2)^3 *
      1*
      3^3 * (1 * 2 * 3)
最后一步中的1 * 2 *3可递归处理。
因为P的倍数与P不互质,所以P的倍数不能直接乘入答案,应当用一个计数器变量g来保存答案中因子P的个数。
我们提前预处理出fac[i] = 1 * 2 * 3 * … * (i – 1) * i mod P,函数calcfac(n)返回n! mod P的值,power(a, b, c)返回ab mod c的值,注意,calcfac(n)算出来的值不包含因子P,我们另外把因子P的个数另外存在g中。
代码如下:
typedef long long LL;
LL calcfac(LL n)
{
if (n < P) return fac[n];
LL s = n / P, t = n % P;
LL res = power(fac[P - 1], s, P); //fac[P - 1]重复出现了s次
res = res * fac[t] % P; //除去s次fac[P – 1]外,剩下的零头
g += n / P; //提出n / P个因子P
res = res* calcfac(n / P) % P; //递归处理
return res;
}
program2 C(m,n)%P(P为质数)
运用program1,算出m!%P,n!%P和(m-n)!%P,已知这三者的答案中因子P的个数为g1,g2,g3。
我们可以得到:

我们不假思索地认为g1-g2-g3>=0(否则C(m,n)算出来就是分数了),而且容易知道calcfac(n)*falcfac(m-n)与P互质(我们除去了P因子)所以可以变成:

这样就可以直接算了。

 
program3 C(m,n)%P(P不一定为质数)
基本思路与program2相同。
对P进行质因数分解,得到P = p1c1 * p2c2 * … * ptct。
对于每个1≤i≤t,以pici为模运行program2,最后用中国剩余定理合并。这里pici不一定为质数,不过只需对原算法稍加修改即可。令modi = pici,fac[i] = 除去pi的倍数外i的阶乘。例如pi = 3,ci = 2,那么fac[10] = 1 * 2 * 4 * 5 * 7 * 8 * 10,除去了3的倍数3、6和9。阶乘依然是以modi为周期的,calcfac(n)与program2主体相同,只是统计因子个数时,应使用ret += n / pi而不是ret += n / modi,递归处理时也应该是calcfac(n / pi)而不是calcfac(n / modi)。
当然,这个算法也有些局限,因为要求fac数组,所以pici要比较小,如果P是一个大质数就不行了。
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<fstream>
#include<algorithm>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<utility>
#include<set>
#include<bitset>
#include<vector>
#include<functional> using namespace std; typedef long long LL;
typedef double DB;
typedef pair<int,int> PII; #define mmst(a,v) memset(a,v,sizeof(a))
#define mmcy(a,b) memcpy(a,b,sizeof(a))
#define re(i,a,b) for(i=a;i<=b;i++)
#define red(i,a,b) for(i=a;i>=b;i--)
#define fi first
#define se second template<class T>inline T sqr(T x){return x*x;}
template<class T>inline void upmin(T &t,T tmp){if(t>tmp)t=tmp;}
template<class T>inline void upmax(T &t,T tmp){if(t<tmp)t=tmp;} const DB EPS=1e-;
inline int dblcmp(DB x){if(abs(x)<EPS)return ;return(x>)?:-;} inline void SetOpen(string s)
{
freopen((s+".in").c_str(),"r",stdin);
freopen((s+".out").c_str(),"w",stdout);
} inline int Getin_Int()
{
int res=,flag=;char z;
for(z=getchar();z!=EOF && z!='-' && !isdigit(z);z=getchar());
if(z==EOF)return ;
if(z=='-'){flag=-flag;z=getchar();}
for(;z!=EOF && isdigit(z);res=res*+z-'',z=getchar());
return res*flag;
}
inline LL Getin_LL()
{
LL res=,flag=;char z;
for(z=getchar();z!=EOF && z!='-' && !isdigit(z);z=getchar());
if(z==EOF)return ;
if(z=='-'){flag=-flag;z=getchar();}
for(;z!=EOF && isdigit(z);res=res*+z-'',z=getchar());
return res*flag;
} const LL maxpici=;
const LL maxcnt=; LL n,m,P;
LL cnt,p[maxcnt+],c[maxcnt+],mod[maxcnt+],euler[maxcnt+],fac[maxcnt+][maxpici+];
LL ans; inline LL power(LL a,LL b,LL Mod)
{
LL x=,y=a;
while(b!=){if(b&)x=x*y%Mod;if(b==)break;y=y*y%Mod;b/=;}
return x;
} inline LL calfac(LL N,LL id,LL &g)
{
if(N<p[id])return fac[id][N];
LL s=N/mod[id],t=N%mod[id],res=;
res=res*power(fac[id][mod[id]-],s,mod[id])%mod[id];
res=res*fac[id][t]%mod[id];
g+=N/p[id];
res=res*calfac(N/p[id],id,g)%mod[id];
return res;
} inline LL solve(LL id)
{
LL g1=,g2=,g3=;
LL fenzi=calfac(m,id,g1),fenmo=calfac(n,id,g2)*calfac(m-n,id,g3)%mod[id];
return fenzi*power(fenmo,euler[id]-,mod[id])%mod[id]*power(p[id],g1-g2-g3,mod[id])%mod[id];
} inline void extend_gcd(LL a,LL &x,LL b,LL &y)
{
if(b==){x=;y=;return;}
LL dx,dy;
extend_gcd(b,dx,a%b,dy);
x=dy;
y=dx-a/b*dy;
} int main()
{
SetOpen("program");
LL i,j,temp;
m=Getin_Int();n=Getin_Int();P=Getin_Int();
if(m<n){puts("0\n");return ;}
temp=P;
re(i,,maxpici)if(temp%i==)
{
++cnt;
p[cnt]=i;
mod[cnt]=;
while(temp%i==){c[cnt]++;mod[cnt]*=i;temp/=i;}
euler[cnt]=mod[cnt]/p[cnt]*(p[cnt]-);
fac[cnt][]=;
re(j,,mod[cnt]-)fac[cnt][j]=(j%p[cnt]==)?fac[cnt][j-]:fac[cnt][j-]*j%mod[cnt];
}
ans=;
re(i,,cnt)
{
temp=solve(i);
LL a=P/mod[i],x,b=mod[i],y;
extend_gcd(a,x,b,y);
ans=(ans+temp*a%P*x%P)%P;
}
ans=(ans%P+P)%P;
cout<<ans<<endl;
return ;
}
 来自:

随机推荐

  1. PE基金的运作模式有哪些?

    一.信托制(1)信托型基金是由基金管理机构与信托公司合作设立,通过发起设立信托受益份额募集资金,然后进行投资运作的集合投资工具(2)信托公司和基金管理机构组成决策委员会实施,共同进行决策(3)在内部分 ...

  2. Hello,world,l'm coming!

    #include<studio.h> int main() { printf("Hello,Word!" l'm coming\n"); return0; }

  3. 实现C++模板类头文件和实现文件分离的方法

    如何实现C++模板类头文件和实现文件分离,这个问题和编译器有关. 引用<<C++primer(第四版)>>里的观点:1)标准C++为编译模板代码定义了两种模型:“包含”模型和“ ...

  4. mysql 建立加密连接

    加密连接可提高数据的安全性,但会降低性能.要进行加密连接,必须满足以下要求: user权限表里要有相关的SSL数据列.如果安装的MySQL服务器是4.0.0版的,user权限表已包含相关的SSL数据列 ...

  5. Docker远程访问get(root)shell姿势

    0x00 概述 在用swarm来管理dockers容器集群时默认开启了2375端口,通过远程访问docker run -H tcp://ip:2375 $command 可任意执行docker命令.这 ...

  6. HTML基础总结<头部>

    重点摘录:HTML head 元素 标签 描述 <head> 定义了文档的信息 <title> 定义了文档的标题 <base> 定义了页面链接标签的默认链接地址 & ...

  7. html拼接数据的时候一定要注意null值的问题

    后台会返回null文本  如果直接拼接 不仅仅格式问题 前台会显示null   如果是图片  用fiddle抓取 还会发现你请求了一个带域名/null的接口     所以要把null格式化为空文本

  8. svn服务器的配置步骤

    1.安装客户端: TortoiseSVN-1.9.3.27038-x64-svn-1.9.3.msi下载地址:http://jaist.dl.sourceforge.net/project/torto ...

  9. Linux 系统库函数coreleft 与sbrk简介

    coreleft   函数名: coreleft 功 能: 返回未使用内存的大小 用 法: unsigned coreleft(void); 程序例: #include <stdio.h> ...

  10. C++中的dynamic_cast和static_cast

    代码: #include <cstdio> #include <iostream> using namespace std; class A{ public: virtual ...