Lucas定理求C(n,m)% P:
typedef long long LL;
LL mod;
inline LL pow(LL a, LL b)//快速幂是为了求逆元
{
LL ans = 1;
for(; b; b >>= 1,a = a * a % mod)
if(b & 1)
ans = ans * a % mod;
return ans;
}
LL farc[1000005];
inline void prepare(LL a)
{
farc[0]=1;
for(LL i = 1; i <= a; ++i)
farc[i]=farc[i-1]*i%mod;
}
inline LL Csmall(LL m, LL n) // C(m,n) = (n!)/(m!*(n-m)!)
{
if(n < m)
return 0;
return farc[n] * pow(farc[m], mod-2) % mod * pow(farc[n-m], mod-2) % mod; // 费马小定理求逆元
}
inline LL C(LL m, LL n)
{
if(n < m)
return 0;
if(!n)
return 1;//Lucas的边界条件
return C(m/mod, n/mod) % mod * Csmall(m%mod, n%mod) % mod; // 上面证明的Lucas定理
}
GCD:
LL gcd(LL a,LL b){
while(b^=a^=b^=a%=b);
return a;
}
Ex-GCD:
int Ex_Gcd(int a,int b,int &x,int &y)
{
if(b==0)
{
x = 1;
y = 0;
return a;
}
int r = exGcd(b,a%b,x,y);
t = x; x = y;
y = t-a/b*y;
return r;
}
Ex_GCD求逆元:
void extgcd(ll a,ll b,ll& d,ll& x,ll& y){
if(!b){ d=a; x=1; y=0;}
else{ extgcd(b,a%b,d,y,x); y-=x*(a/b); }
}
ll inverse(ll a,ll n){
ll d,x,y;
extgcd(a,n,d,x,y);
return d==1?(x+n)%n:-1;
}
快速幂:
ll pow_mod(ll x, ll n, ll mod){
ll res=1;
while(n>0){
if(n&1)res=res*x%mod;
x=x*x%mod;
n>>=1;
}
return res;
}
根据费马小定理,可用快速幂求逆元。
欧拉筛(线性):
bool IsPrime[10000001];
int Pri[2000001],PriN;
int FindPrime ( int MaxN ) {
for( int i = 2 ; i <= MaxN ; ++i ){
if( IsPrime[ i ] )
Pri[ PriN++ ]=i; //将这句话放在下面的循环前以保证PriN和Pri值的完整性
for(int j=0;j<PriN;++j){
if( i*Pri[ j ] > MaxN )
break; //当过大了就跳出
IsPrime[ i * Pri[ j ] ] = 0;
//筛去素数
if( i % Pri[ j ] == 0 ) break;
//这里是关键,如果i是一个合数(这当然是允许的)而且i mod prime[j] = 0
//那么跳出,因为i*prime[ (- over -)j ]一定已经被筛去了,被一个素因子比i小的数
}
}
}
大于等于5的质数一定和6的倍数相邻(适合访问量较小时):
bool isprime(ll n){
if(n == 1) return 0;
if(n == 2 || n == 3) return 1;
if(n%6 != 5 && n%6 != 1) return 0;
for(ll i=5 ; i*i <= n; i += 6){
if(n%i == 0 || n%(i+2) == 0) return 0;
}
return 1;
}
KMP求两字符串是否匹配:
const int maxn = 2e6+100;
int n,m;
char s[maxn];
char t[maxn];
int next[maxn];
void getnext(){
int i=0,j=-1;
next[0] = -1;
while(i<m){
if(j == -1 || t[i] == t[j])
next[++i] = ++j;
else j = next[j];
}
// for(int i=1;i<=m;++i) printf("%d ", next[i]);
// puts("");
}
bool kmp(){
getnext();
int i=0,j=0;
while(i<n){
if(j == -1 || s[i] == t[j]){
i++;j++;
}
else j = next[j];
if(j == m) return 1;
}
return 0;
}
Manacher求最长回文字串:
const int N = 3e6;
char s[N];
char s_new[N];
int p[N];
int Init()
{
int len = strlen(s);
s_new[0] = '$';
s_new[1] = '#';
int j = 2;
for (int i = 0; i < len; i++)
{
s_new[j++] = s[i];
s_new[j++] = '#';
}
s_new[j] = '\0'; // 别忘了哦
return j; // 返回 s_new 的长度
}
int Manacher()
{
int len = Init(); // 取得新字符串长度并完成向 s_new 的转换
int max_len = -1; // 最长回文长度
int id;
int mx = 0;
for (int i = 1; i < len; i++)
{
if (i < mx)
p[i] = min(p[2 * id - i], mx - i); // 需搞清楚上面那张图含义, mx 和 2*id-i 的含义
else
p[i] = 1;
while (s_new[i - p[i]] == s_new[i + p[i]]) // 不需边界判断,因为左有'$',右有'\0'
p[i]++;
// 我们每走一步 i,都要和 mx 比较,我们希望 mx 尽可能的远,这样才能更有机会执行 if (i < mx)这句代码,从而提高效率
if (mx < i + p[i])
{
id = i;
mx = i + p[i];
}
max_len = max(max_len, p[i] - 1);
}
return max_len;
}