Asm.Def点大兵
syzoj上的题,收货很多,orz天天学长
原题:
Asm.Def奉命组建一支m人的特种作战小队前往圣迭戈。他有n名候选人,可以在其中任意挑选。由于小队中每个人都有独特的作用,所以次序不同的两种选法被认为是不同的方案。由于方案数可能非常大,Asm.Def只需要知道它模p的值。
100%:n<=10^18,m<=10^5,p<=10^18
很明显就是求排列……
然而n<=10^18用longlong乘的话会炸
用高精度的话取模会非常不好搞,而且常数似乎也不滋瓷
syzoj可以直接查看别人代码,然后就看到了这么个黑科技:
1 LL f(LL x,LL y) 2 { 3 LL tmp=0; 4 while(y) 5 { 6 if(y&1) 7 tmp=(tmp+x)%p; 8 y=y>>1; 9 x=(x<<1)%p; 10 } 11 return tmp; 12 }
一眼看上去挺像快速幂的,看不懂,请教天天学长
然后就知道这是为了防止炸longlong的二进制分配乘
原理就是将其中一个乘数分解成二进制,比如5 * 17就是5 * (16 + 1),5 * 23 就是 5 * (16 + 4 + 2 + 1)
逐步膜就不会炸longlong
新姿势get√
还要再强调一下,如果使用longlong就要逐步走一遍,所有遇到的变量全部换成longlong,尤其是函数的参数和返回值,有时候甚至循环变量也会使用longlong,比如for(long long i=n-m+1;i<=n;i++),其中n,m都是longlong
代码:
1 //__3_108_120_116__ 2 #include<iostream> 3 #include<cstdio> 4 #include<algorithm> 5 #include<cstring> 6 #include<cmath> 7 using namespace std; 8 long long m,n,mo; 9 long long ans=1; 10 long long multiply01(long long x,long long y){//参数传的是longlong!!! 11 long long z=0; 12 while(y){ 13 if(y&1) z=(z+x)%mo; 14 y=y>>1; 15 x=(x<<1)%mo; 16 } 17 return z; 18 } 19 int main(){ 20 cin>>n>>m>>mo; 21 for(long long i=n-m+1;i<=n;i++)//要注意这里n会很大所以i要longlong! 22 ans=multiply01(ans,i); 23 cout<<ans<<endl; 24 return 0; 25 }