Asm.Def点大兵

时间:2023-12-15 13:14:50

syzoj上的题,收货很多,orz天天学长

原题:

Asm.Def奉命组建一支m人的特种作战小队前往圣迭戈。他有n名候选人,可以在其中任意挑选。由于小队中每个人都有独特的作用,所以次序不同的两种选法被认为是不同的方案。由于方案数可能非常大,Asm.Def只需要知道它模p的值。

100%:n<=10^18,m<=10^5,p<=10^18

很明显就是求排列……

然而n<=10^18用longlong乘的话会炸

用高精度的话取模会非常不好搞,而且常数似乎也不滋瓷

syzoj可以直接查看别人代码,然后就看到了这么个黑科技:

 LL f(LL x,LL y)
{
LL tmp=;
while(y)
{
if(y&)
tmp=(tmp+x)%p;
y=y>>;
x=(x<<)%p;
}
return tmp;
}

一眼看上去挺像快速幂的,看不懂,请教天天学长

然后就知道这是为了防止炸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

代码:

 //__3_108_120_116__
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
long long m,n,mo;
long long ans=;
long long multiply01(long long x,long long y){//参数传的是longlong!!!
long long z=;
while(y){
if(y&) z=(z+x)%mo;
y=y>>;
x=(x<<)%mo;
}
return z;
}
int main(){
cin>>n>>m>>mo;
for(long long i=n-m+;i<=n;i++)//要注意这里n会很大所以i要longlong!
ans=multiply01(ans,i);
cout<<ans<<endl;
return ;
}