排列:
不可重复排列:
可重复排列:从n个取可重复k个排列数为:
圆排列:
错位排列:
指数母函数定义:
组合:
不可重复组合:
可重复组合:
不相邻组合:从n个取m个不相邻组合数为:
组合常用公式:
帕斯卡恒等式:
普通母函数定义:
扫描二维码关注公众号,回复:
6082528 查看本文章
常见数列:
斐波那契数列:
卡特兰数列:
递归公式1:
递归公式2:
组合公式1:
组合公式2:
排列/组合数取模
ll fac[maxn],inv[maxn];
ll pow_mod(ll a, ll n) {
ll res = 1;
while(n) {
if(n&1) res = res*a % mod;
a = a*a % mod;
n >>= 1;
}
return res;
}
void init() {
fac[0] = 1;
for(int i=1; i < maxn; i++) {
fac[i] = fac[i-1]*i % mod;
}
}
ll C(ll n, ll r) {
return fac[n] * pow_mod(fac[r]*fac[n-r]%mod, mod-2) % mod;
return fac[n] * pow_mod(fac[n-r], mod-2) % mod; // 排列数取模
}
错排问题
/*------------------------------------------------------------
问题: 十本不同的书放在书架上。
现重新摆放,使每本书都不在原来放的位置。有几种摆法?
这个问题推广一下,就是错排问题,是组合数学中的问题之一。
考虑一个有n个元素的排列,若一个排列中所有的元素都不在自己原来的位置上,
那么这样的排列就称为原排列的一个错排。 n个元素的错排数记为D(n)。
研究一个排列错排个数的问题,叫做错排问题或称为更列问题。
------------------------------------------------------------*/
// dp[1]=0, dp[2]=1
// dp[i] = (i - 1)*(dp[i - 1] + dp[i - 2]); i > 2
ll D(int n) {
ll a = 0, b = 1, c;
if(n < 3) return n-1;
for (int i = 3; i <= n; i++) {
c = ((i - 1) * 1ll * (a + b)) % mod;
a = b;
b = c;
}
return c;
}
卡特兰数列
/*--------------------------------------------------
常见用法:
卡特兰数的应用都可以归结到一种情况:有两种操作,
分别为操作一和操作二,它们的操作次数相同,
都为 N,且在进行第 K 次操作二前必须先进行至少 K 次操作一,
问有多少中情况?结果就Catalan(N)。
1.给定n个数,有多少种出栈序列
2.n个结点的二叉树有多少种构型
3.在nn的格子中,只在下三角行走,每次横或竖走一格,有多少种走法
4.将一个凸n+2边型剖分成三角形的方法数
5.在圆上选择2n个点,将这些点成对相连使得到的n条线段不相交的方法数
6.n个长方形填充一个高度为n的阶梯状图形的方法数
7.括号化问题,左括号和右括号各有n个时,合法的括号表达式的种类
8.有n+1个数连乘,乘法顺序的种类
9.n位二进制中,有m个0,其余为1,有h[C(n,m)-C(n,m-1)]种
--------------------------------------------------*/
ll fat[N];
ll inv[N];
ll finv[N];
void Init(){
for(ll i=2,inv[1]=1; i < N; i++)
inv[i] = ((mod- mod/i) * 1ll * inv[mod%i])%mod;
for(ll i=1,fat[0]=1,finv[0]=1; i < N; i++){
fat[i] = (fat[i-1] * i) % mod;
finv[i] = (finv[i-1]*inv[i]) % mod;
}
}
ll C(ll n,ll m){
ll res = 1;
res = res*fat[n] % mod;
res = (res * finv[m]%mod * finv[n-m] % mod);
return res;
}
ll Ctl(ll n){
return (C(2*n,n)-C(2*n,n-1)+mod)%mod;
}
普通母函数
/*--------------------------------------------------
经典例题:有质量为1、2、4的砝码分别为1、3、2枚
问:可以称出多少不同质量?要称出质量为3有几种方式?
解:构造函数G(x)=(1+x)*(1+x^2+x^4+x^6)*(1+x^4+x^8)
其中第二个括号中X^6表示:用第二个物品质量3枚,以此类推
代码为模拟构造函数计算过程 比如:
(1+x) * (1+x^2+x^4+x^6) * (1+x^4+x^8)
= (1+x^2+x^4+x^6 + x+x^3+x^5+x^7) * (1+x^4+x^8)
= ...
--------------------------------------------------*/
// v[i]第i个未知量价值,s/e:物品的最大/最小个数
int v[MAX], s[MAX], e[MAX];
// a为最终结果,b为中间结果。
int a[MAX], b[MAX];
// P为可能出现的最大指数
int P;
void mu(int n) { // n为种类数
memset(a,0,sizeof(a));
a[0]=1;
for(int i=1; i <= n; i++) { //循环每个因子
memset(b, 0, sizeof(b));
// j为第i种物品可能的数量,j*v[i]即第i个括号中第j项的系数
for (int j=n1[i]; j<=n2[i] && j*v[i]<=P; j++) {
// 计算对前i-1项相乘的结果的第k项系数
for (int k=0; k+j*v[i] <= P; k++)
b[k+j*v[i]] += a[k];
}
memcpy(a, b, sizeof(b)); // 将b数组存回a数组
}
}
指数母函数
/*--------------------------------------------------
经典例题:假设有多个元素,其中a1重复n1次,a2重复n2次,
a3重复n3次。从中取r个排列,求其排列数。
构造函数G(x) = (1+x/1!+x^2/2!+...+x^n1/n1!) *
(1+x/1!+...+x^n2/n2!) *(1+x/1!+...+x^n3/n3!)
根据普通母函数不难理解其中每个值的意思
--------------------------------------------------*/
ll fac[N]; // fac[i]表示第i个阶乘值
int a[N]; //1~n每种的个数
double c1[N],c2[N]; // double
void cal(int n,int m) { //有n种,取m个
memset(c1, 0, sizeof(c1));
memset(c2, 0, sizeof(c2));
c1[0] = 1.0/fac[0];
for(int i=1; i<=n; i++) {
for(int j=0; j<=m; j++) {
for(int k=0; k+j<=m && k<=a[i]; k++)
c2[k+j] += c1[j]/fac[k];
}
for(int j=0; j<=m; j++) {
c1[j] = c2[j];
c2[j]=0;
}
}
}
ans=c1[m]*fac[m]; //取m个时的多重排列数
Polya计算
/*==================================================*\
| c种颜色的珠子, 组成长为s的项链, 项链没有方向和起始位置;
\*==================================================*/
int gcd (int a, int b) { return b ? gcd(b,a%b) : a; }
int main (void){
int c, s;
while (scanf("%d%d", &c, &s)==2) {
int k;
long long p[64]; p[0] = 1; // power of c
for (k=0 ; k<s ; k++) p[k+1] = p[k] * c;
// reflection part
long long count = s&1 ? s * p[s/2 + 1] : (s/2) * (p[s/2] + p[s/2 + 1]);
// rotation part
for (k=1 ; k<=s ; k++) count += p[gcd(k, s)];
count /= 2 * s;
cout << count << '\n';
}
return 0;
}
快速傅里叶变换(FFT)
typedef complex<double> cd; //复数类的定义
const int maxl=2094153; //nlogn的最大长度
const double PI=3.14159265358979;
cd a[maxl],b[maxl]; //用于储存变换的中间结果
int rev[maxl]; //用于储存二进制反转的结果
void getrev(int bit) {
//高位决定二进制数的大小
for(int i=0; i<(1<<bit); i++){
rev[i] = (rev[i>>1]>>1)|((i&1)<<(bit-1));
}//能保证(x>>1)<x,满足递推性质
}
void fft(cd* a,int n,int dft){//变换主要过程
for(int i=0; i<n; i++){//按照二进制反转
//保证只把前面的数和后面的数交换,(否则数组会被翻回来)
if(i<rev[i])
swap(a[i],a[rev[i]]);
}
for(int step=1; step<n; step<<=1){ //枚举步长的一半
cd wn=exp(cd(0, dft*PI/step));//计算单位复根
for(int j=0; j<n; j+=step<<1){ //对于每一块
//!!每一块都是一个独立序列,都是以零次方位为起始的
cd wnk(1,0);
for(int k=j; k<j+step; k++){ //蝴蝶操作处理这一块
cd x = a[k];
cd y = wnk*a[k+step];
a[k] = x+y;
a[k+step] = x-y;
wnk *= wn; //计算下一次的复根
}
}
}
if(dft==-1){ //如果是反变换,则要将序列除以n
for(int i=0;i<n;i++)
a[i]/=n;
}
}
int output[maxl];
char s1[maxl], s2[maxl];
void getmuti() { //计算高精度大数乘法,输入两个数a,b
scanf("%s%s",s1,s2); //读入两个数
int l1 = strlen(s1),l2=strlen(s2); //就算"次数界"
int bit=1, s=2; //s表示分割之前整块的长度
for(bit=1; (1<<bit)<l1+l2-1; bit++){
s <<= 1; //找到第一个二的整数次幂使得其可以容纳这两个数的乘积
}
for(int i=0; i<l1; i++){ //第一个数装入a
a[i] = (double)(s1[l1-i-1]-'0');
}
for(int i=0; i<l2; i++){ //第二个数装入b
b[i] = (double)(s2[l2-i-1]-'0');
}
getrev(bit); fft(a,s,1); fft(b,s,1); //dft
for(int i=0; i<s; i++) a[i]*=b[i]; //对应相乘
fft(a, s, -1); //idft
for(int i=0; i<s; i++){ //还原成十进制数
output[i] += (int)(a[i].real()+0.5); //注意精度误差
output[i+1] += output[i]/10;
output[i] %= 10;
}
int i;
for(i=l1+l2; !output[i]&&i>=0; i--); //去掉前导零
if(i==-1) printf("0"); //特判长度为0的情况
for(; i>=0; i--){ //输出这个十进制数
printf("%d",output[i]);
}
putchar('\n');
}
void getpoly() { //计算多项式乘法
int n, m;
scanf("%d%d",&n,&m); //输入量多项式最高项次数
//读入第一个多项式的系数(a0+a1*x+a2*x^2+a3*x^3+.....+an*x^n)
for(int i=0; i<=n; i++) scanf("%lf",&a[i].real());
//读入第二个多项式的系数(b0+b1*x+b2*x^2+b3*x^3+.....+bn*x^n)
for(int i=0; i<=m; i++) scanf("%lf",&b[i].real());
int bit=0, s=1; //s表示分割之前整块的长度
for(s=1; s<=n+m; s<<=1)
bit++;
getrev(bit); fft(a,s,1); fft(b,s,1);//dft
for(int i=0; i<s; i++) a[i] *= b[i];//对应相乘
fft(a, s, -1); //idft
for(int i=0; i<=n+m; i++)
//表示乘完多项式各项的系数,(a0+a1*x+a2*x^3...)
printf("%d ",(int)(a[i].real()+0.5));
}
int main() {
getmuti(); //10*10=100
getpoly(); //(1+2x)*(1+2x+x2)=1+4x+5x2+2x3
return 0;
}