老夫聊发少年狂,碱金属,丢池塘。浮溶游响|

Yaosicheng124

园龄:1年2个月粉丝:8关注:11

组合数学

逆元

ix=1,则 i1=x

递推求乘法逆元

p,i,a=pi,b=pmodi,b0ai+b=pai+b0(modp)ib+1a0(modp)ib1a(modp)bia(modp)i1(a)b1(modp)

invi=i1,则我们可以由上面的结论得到 invi=piinvpmodimodp

代码

void C() {
  inv[1] = 1;
  for(int i = 2; i < MAXV; ++i) {
    inv[i] = 1ll * (p - p / i) * inv[p % i] % p;
  }
}

C();

平方求组合数

可以发现,Cnm 有两种情况:

  • 选最后一个:Cn1m1
  • 不选最后一个:Cn1m

所以 Cnm=Cn1m1+Cn1m。(实际上这就是杨辉三角)

代码

void C() {
  for(int i = 0; i < MAXV; ++i) {
    c[i][0] = 1;
    for(int j = 1; j <= i; ++j) {
      c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % MOD;
    }
  }
}

预处理求组合数

众所周知,Cnm=n!m!(nm)!。所以我们只需预处理出阶乘的逆元。

fi=i!,gi=i!1,则:

fi=fi1imodpgi=gi1invimodpCnm=fngmgnmmodp

void C() {
  inv[1] = f[0] = Inv[0] = 1;
  for(int i = 1; i < MAXV; ++i) {
    f[i] = 1ll * f[i - 1] * i % p;
    inv[i] = (i > 1 ? 1ll * (p - p / i) * inv[p % i] % p : 1);
    Inv[i] = 1ll * Inv[i - 1] * inv[i] % p;
  }
}

C();

题目

CSES P1715

题目描述

给定一个字符串 S,求将其重新排列后能得到多少种不同的字符串。

思路

首先在不考虑字符相同的情况下答案明显就是 |S|!,而每种相同字符会重复计算 cntx! 次,其中 cntx 表示字符 x 的出现次数,所以答案为:|S|!i=025cnti!1

时空复杂度均为 O(N)

细节

无。

代码

#include<bits/stdc++.h>
using namespace std;

const int MAXV = 1000001, MOD = 1000000007;

int f[MAXV], nv[MAXV], inv[MAXV], cnt[26], ans;
string s;

void C() {
  f[0] = f[1] = nv[1] = inv[0] = inv[1] = 1;
  for(int i = 2; i < MAXV; ++i) {
    f[i] = 1ll * f[i - 1] * i % MOD;
    nv[i] = 1ll * (MOD - MOD / i) * nv[MOD % i] % MOD;
  }
  for(int i = 2; i < MAXV; ++i) {
    inv[i] = 1ll * inv[i - 1] * nv[i] % MOD;
  }
}

int main() {
  ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
  C();
  cin >> s;
  for(int i = 0; i < int(s.size()); ++i) {
    cnt[s[i] - 'a']++;
  }
  ans = f[s.size()];
  for(int i = 0; i < 26; ++i) {
    ans = 1ll * ans * inv[cnt[i]] % MOD;
  }
  cout << ans;
  return 0;
}

CSES P1716

题目描述

N 个小朋友和 M 个苹果,求有多少中分配方案。

思路

可以看做是 M 个苹果之间有 M1 个空位,有 N1 个板子要插在空位中,相邻两块板子之间的苹果就是属于同一个小朋友的。可是小朋友可以不拿苹果,所以我们再加入 N 个虚拟苹果,使得每个小朋友都能拿到苹果,所以答案为 CN+M1N1

时空复杂度均为 O(N)

细节

代码

#include<bits/stdc++.h>
using namespace std;

const int MAXV = 2000001, MOD = 1000000007;

int n, m, f[MAXV], nv[MAXV], inv[MAXV], cnt[26], ans;

void C() {
  f[0] = f[1] = nv[1] = inv[0] = inv[1] = 1;
  for(int i = 2; i < MAXV; ++i) {
    f[i] = 1ll * f[i - 1] * i % MOD;
    nv[i] = 1ll * (MOD - MOD / i) * nv[MOD % i] % MOD;
  }
  for(int i = 2; i < MAXV; ++i) {
    inv[i] = 1ll * inv[i - 1] * nv[i] % MOD;
  }
}

int main() {
  ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
  C();
  cin >> n >> m;
  cout << ((1ll * f[m + n - 1] * inv[n - 1]) % MOD * inv[m]) % MOD;
  return 0;
}

GYM 104386 C

题目描述

有一个数组 A={1,1,},每次对 A 进行前缀和,求 K 次操作后 AN 的值。

思路

令第 i 次第 j 项为 fi,j,可以得到 fi,j=fi,j1+fi1,j,现在我们转换一下坐标系,变为斜方向的,即 fi,j=fi1,j1+fi1,j,很容易发现这就是组合数的递推式。因为转换了坐标系,所以答案为 CN+K1N1

时空复杂度均为 O(N+K)

细节

注意第一维是 1 下标,第二维是 0 下标。

代码

#include<bits/stdc++.h>
using namespace std;

const int MAXV = 2000001, MOD = 1000000007;

int t, n, k, f[MAXV], nv[MAXV], inv[MAXV];

void C() {
  f[0] = f[1] = nv[1] = inv[0] = inv[1] = 1;
  for(int i = 2; i < MAXV; ++i) {
    f[i] = 1ll * f[i - 1] * i % MOD;
    nv[i] = 1ll * (MOD - MOD / i) * nv[MOD % i] % MOD;
  }
  for(int i = 2; i < MAXV; ++i) {
    inv[i] = 1ll * inv[i - 1] * nv[i] % MOD;
  }
}

void Solve() {
  cin >> n >> k;
  int x = n + k - 1, y = n - 1;
  cout << ((1ll * f[x] * inv[y]) % MOD * inv[x - y]) % MOD << "\n";
}

int main() {
  ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
  C();
  cin >> t;
  while(t--) {
    Solve();
  }
  return 0;
}

CSES P1717

题目描述

N 个小朋友送礼物,不能给自己送礼物,求每个小朋友都收到礼物的方案数。

思路

由于每个小朋友都要收到礼物,所以这就是求错排数,使用 DP。

dpx 表示长度为 x 的错排数,A1=i(2ix)。则第 i 位上有两种情况:

  • Ai=1,则方案数为 dpx2,因为 A1Ai 已经不会对答案造成影响。
  • Ai1,则方案数为 dpx1,因为 A1 已经没有作用,可以把 Ai 看做 A1

所以 dpx=(x1)(dpx1+dpx2)

细节

dp0=1,dp1=0

代码

#include<bits/stdc++.h>
using namespace std;

const int MAXV = 2000001, MOD = 1000000007;

int n, f[MAXV], nv[MAXV], inv[MAXV], dp[MAXV];

void F() {
  f[0] = f[1] = nv[1] = inv[0] = inv[1] = 1;
  for(int i = 2; i < MAXV; ++i) {
    f[i] = 1ll * f[i - 1] * i % MOD;
    nv[i] = 1ll * (MOD - MOD / i) * nv[MOD % i] % MOD;
  }
  for(int i = 2; i < MAXV; ++i) {
    inv[i] = 1ll * inv[i - 1] * nv[i] % MOD;
  }
}

int C(int x, int y) {
  return ((1ll * f[x] * inv[y]) % MOD * inv[x - y]) % MOD;
}

int A(int x, int y) {
  return (1ll * f[x] * inv[y]) % MOD;
}

int Pow(int a, int b) {
  int res = 1;
  while(b) {
    if(b & 1) {
      res = (1ll * res * a) % MOD;
    }
    a = (1ll * a * a) % MOD;
    b >>= 1;
  }
  return res;
}

int main() {
  ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
  F();
  cin >> n;
  dp[0] = 1, dp[1] = 0;
  for(int i = 2; i <= n; ++i) {
    dp[i] = 1ll * (i - 1) * (dp[i - 1] + dp[i - 2]) % MOD;
  }
  cout << dp[n];
  return 0;
}

Luogu P4071

题目描述

求有多少种 1N 的排列 A,使得恰好有 Mi 满足 Ai=i

思路

由于有 M 个位置 Ai=i,则剩下的肯定不满足,即错排,所以方案数为 dpNMCNM

时空复杂度均为 O(N)

细节

无。

代码

#include<bits/stdc++.h>
using namespace std;

const int MAXV = 2000001, MOD = 1000000007;

int t, n, m, f[MAXV], nv[MAXV], inv[MAXV], dp[MAXV];

void F() {
  f[0] = f[1] = nv[1] = inv[0] = inv[1] = 1;
  for(int i = 2; i < MAXV; ++i) {
    f[i] = 1ll * f[i - 1] * i % MOD;
    nv[i] = 1ll * (MOD - MOD / i) * nv[MOD % i] % MOD;
  }
  for(int i = 2; i < MAXV; ++i) {
    inv[i] = 1ll * inv[i - 1] * nv[i] % MOD;
  }
  dp[0] = 1, dp[1] = 0;
  for(int i = 2; i <= MAXV; ++i) {
    dp[i] = 1ll * (i - 1) * (dp[i - 1] + dp[i - 2]) % MOD;
  }
}

int C(int x, int y) {
  return ((1ll * f[x] * inv[y]) % MOD * inv[x - y]) % MOD;
}

int A(int x, int y) {
  return (1ll * f[x] * inv[y]) % MOD;
}

int Pow(int a, int b) {
  int res = 1;
  while(b) {
    if(b & 1) {
      res = (1ll * res * a) % MOD;
    }
    a = (1ll * a * a) % MOD;
    b >>= 1;
  }
  return res;
}

int main() {
  ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
  F();
  cin >> t;
  while(t--) {
    cin >> n >> m;
    cout << 1ll * dp[n - m] * C(n, n - m) % MOD << "\n";
  }
  return 0;
}

本文作者:yaosicheng124

本文链接:https://www.cnblogs.com/yaosicheng124/p/18129812

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   Yaosicheng124  阅读(19)  评论(1编辑  收藏  举报
  1. 1 Minecraft C418
Minecraft - C418
00:00 / 00:00
An audio error has occurred.

暂无歌词

加载中…

{{tag.name}}

{{tran.text}}{{tran.sub}}
无对应文字
有可能是
{{input}}
尚未录入,我来提交对应文字
评论
收藏
关注
推荐
深色
回顶
收起
点击右上角即可分享
微信分享提示