37.排列组合
不可重复排列数
n
!
(
n
−
r
)
!
\frac{n!}{(n-r)!}
(n−r)!n!
可重复排列数
n
r
n^r
nr
圆排列数
n
!
r
(
n
−
r
)
!
\frac{n!}{r(n-r)!}
r(n−r)!n!
无限多重集排列,k个不同元素
k
r
k^r
kr
有限多重集排列,k个不同元素
n
!
n
1
!
n
2
!
.
.
.
n
k
!
\frac{n!}{n_1!n_2!...n_k!}
n1!n2!...nk!n!
无限多重集组合,k个不同元素
C
r
+
k
−
1
r
C_{r+k-1}^r
Cr+k−1r
38.鸽巢原理
n+1只鸽子住n个巢,至少有一个巢里有至少2只鸽子
kn+1只鸽子住n个巢,至少有一个巢里有至少k+1只鸽子
如果将
q
1
+
q
2
+
.
.
.
q
n
−
n
+
1
q_1+q_2+...q_n-n+1
q1+q2+...qn−n+1个物体放入n个盒子,那么或者第1个盒子至少有
q
1
q_1
q1个物体,或者第2个盒子至少有
q
2
q_2
q2个物体,…,或者第n个盒子至少有
q
n
q_n
qn个物体
Ramsey定理:在6人(或更多)中,或者有3人,每两人都互相认识,或者有3人,每两人都不认识
39.杨辉三角与二项式定理
C
n
r
=
(
n
r
)
=
n
!
r
!
(
n
−
r
)
!
C_n^r=\binom{n}{r}=\frac{n!}{r!(n-r)!}
Cnr=(rn)=r!(n−r)!n!
杨辉三角第n+1行第r+1列数,
(
1
+
x
)
n
(1+x)^n
(1+x)n展开后第r+1项系数
(
a
+
b
)
n
=
∑
r
=
0
n
C
n
r
a
r
b
n
−
r
(a+b)^n=\sum\limits_{r=0}^nC_n^ra^rb^{n-r}
(a+b)n=r=0∑nCnrarbn−r
C
n
r
=
C
n
−
1
r
−
1
+
C
n
−
1
r
C_n^r=C_{n-1}^{r-1}+C_{n-1}^r
Cnr=Cn−1r−1+Cn−1r
C
n
0
+
C
n
1
+
.
.
.
+
C
n
n
=
2
n
C_n^0+C_n^1+...+C_n^n=2^n
Cn0+Cn1+...+Cnn=2n
40.组合数(递推)
#include<bits/stdc++.h>
using namespace std;
const int N = 2010, mod = 1e9 + 7;
int c[N][N];
void init(){
for (int i = 0; i < N; i ++ )
for (int j = 0; j <= i; j ++ )
if (j==0) c[i][j] = 1;
else c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod;
}
int main(){
int n;
init();
scanf("%d", &n);
while (n -- ){
int a, b;
scanf("%d%d", &a, &b);
printf("%d\n", c[a][b]);
}
return 0;
}
41.组合数(费马小定理/预处理)
预处理 O ( n ) O(n) O(n)
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll mod = 998244353;
const ll N = 1e6 + 10;
ll jc[N], jcn[N],inv[N];;
//ll ksm(ll a, ll b) {
// ll res = 1;
// a = a % mod;
// while (b) {
// if (b & 1) res = (res * a) % mod;
// a = (a * a) % mod;
// b >>= 1;
// }
// return res;
//}
//费马小定理
//ll ny(ll x) {
// return ksm(x, mod - 2);
//}
void pre_ny(ll n, ll p) {
inv[1] = 1;
for (ll i = 2; i <= n; i++) inv[i] = (p - p / i) * (inv[p % i]) % p;//线性求逆元
}
ll C(ll n, ll m) {
return jc[n] * jcn[m] % mod * jcn[n - m] % mod;
}
int main() {
pre_ny(N, mod);
jc[0] = jcn[0] = 1;
for (ll i = 1; i <= N; i++) {
jc[i] = (jc[i - 1] * i) % mod;
jcn[i] = (jcn[i - 1] * inv[i]) % mod;
}
ll t, n, m;
cin>>t;
while (t--) {
cin >> n >> m;
cout<< C(n, m)<<'\n';
}
return 0;
}
42.卢卡斯定理
对于质数 p,有
C
n
r
≡
C
n
m
o
d
p
r
m
o
d
p
⋅
C
n
/
p
r
/
p
(
m
o
d
p
)
C_n^r\equiv C_{n\ mod\ p}^{r\ mod\ p}\cdot C_{n/p}^{r/p}(mod\ p)
Cnr≡Cn mod pr mod p⋅Cn/pr/p(mod p)
P3807 【模板】卢卡斯定理/Lucas 定理
给定整数
n
,
m
,
p
n, m, p
n,m,p 的值,求出
C
n
+
m
n
m
o
d
p
C_{n + m}^n \bmod p
Cn+mnmodp 的值。
输入数据保证
p
p
p 为质数。
注:
C
C
C 表示组合数。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define P pair<int,int>
const double eps = 1e-8;
const ll INF = 1e18;
const ll N = 1e5 + 10;
const ll mod = 1e9 + 7;
ll jc[N];
ll ksm(ll a, int b, int p) {
a %= p;
ll res = 1;
while (b) {
if (b & 1) res = (res * a) % p;
a = (a * a) % p;
b >>= 1;
}
return res;
}
ll C(int n, int r, int p) {
if (r > n) return 0;
return jc[n] * ksm(jc[r], p - 2, p) % p * ksm(jc[n - r], p - 2, p) % p;
}
ll lucas(int n, int r, int p) {
if (r == 0) return 1;
return C(n % p, r % p, p) * lucas(n / p, r / p, p) % p;
}
void solve() {
int n, m, p;
cin >> n >> m >> p;
jc[0] = 1;
for (int i = 1; i <= p; ++i) jc[i] = (jc[i - 1] * i) % p;
cout << lucas(n + m, n, p) << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int _t;
//_t = 1;
cin >> _t;
while (_t--) {
solve();
}
return 0;
}
43.扩展卢卡斯定理
{
C
n
m
m
o
d
p
1
α
1
C
n
m
m
o
d
p
2
α
2
.
.
.
.
.
.
\left\{\begin{aligned}C_n^m \bmod\ {p_1^{\alpha_1}} \\C_n^m \bmod\ {p_2^{\alpha_2}}\\ ......\end{aligned} \right.
⎩
⎨
⎧Cnmmod p1α1Cnmmod p2α2......
=
n
!
P
x
m
!
P
y
(
n
−
m
)
!
P
z
P
x
−
y
−
z
m
o
d
P
k
=\frac {\frac {n!}{P^{x}}}{\frac {m!}{P^{y}}\frac {(n-m)!}{P^{z}}}P^{x-y-z}\bmod{P^k}
=Pym!Pz(n−m)!Pxn!Px−y−zmodPk
=
f
(
n
)
f
(
m
)
f
(
n
−
m
)
P
g
(
n
)
−
g
(
m
)
−
g
(
n
−
m
)
m
o
d
P
k
=\frac {f(n)}{f(m)f(n-m)}P^{g(n)-g(m)-g(n-m)}\bmod{P^k}
=f(m)f(n−m)f(n)Pg(n)−g(m)−g(n−m)modPk
f
(
n
)
=
f
(
⌊
n
P
⌋
)
(
∏
i
=
1
,
i
≢
0
(
m
o
d
P
)
P
k
i
)
⌊
n
P
k
⌋
(
∏
i
=
P
k
⌊
n
P
k
⌋
,
i
≢
0
(
m
o
d
P
)
n
i
)
\ f(n)=f(\lfloor \frac nP\rfloor)(\prod\limits_{i=1,i\not\equiv 0\pmod {P}}^{P^k}i)^{\lfloor \frac n{P^k}\rfloor}(\prod\limits_{i=P^k\lfloor \frac n{P^k}\rfloor,i\not\equiv 0\pmod {P}}^ni)
f(n)=f(⌊Pn⌋)(i=1,i≡0(modP)∏Pki)⌊Pkn⌋(i=Pk⌊Pkn⌋,i≡0(modP)∏ni)
g
(
n
)
=
⌊
n
P
⌋
+
g
(
⌊
n
P
⌋
)
g(n)=\lfloor \frac nP\rfloor+g(\lfloor \frac nP\rfloor)
g(n)=⌊Pn⌋+g(⌊Pn⌋)
P4720 【模板】扩展卢卡斯定理/exLucas
求
C
n
m
m
o
d
p
{\mathrm{C}}_n^m \bmod{p}
Cnmmodp
其中
C
\mathrm{C}
C 为组合数。
O ( log P n ) O(\log_Pn) O(logPn)
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define P pair<int,int>
const double eps = 1e-8;
const ll mod = 1e9 + 7;
const ll INF = 1e18;
const ll N = 1e5 + 10;
ll n, m, p;
ll prime[N], cnt, c[N];
ll ksm(ll a, ll b, ll p) {
ll res = 1;
while (b) {
if (b & 1) res = res * a % p;
a = a * a % p;
b >>= 1;
}
return res;
}
ll exgcd(ll a, ll b, ll& x, ll& y) {
if (!b) {
x = 1, y = 0;
return a;
}
ll t = exgcd(b, a % b, x, y);
ll x0 = x, y0 = y;
x = y0;
y = x0 - a / b * y0;
return t;
}
ll ny(ll n, ll p) {
ll x, y;
exgcd(n, p, x, y);
return (x % p + p) % p;
}
ll f(ll n, ll p, ll pr) {
if (n == 0) return 1;
ll rou = 1, rem = 1;
for (ll i = 1; i <= p; ++i) {
if (i % pr) rou = rou * i % p;
}
rou = ksm(rou, n / p, p);
for (ll i = p * (n / p); i <= n; ++i) {
if (i % pr) rem = rem * (i % p) % p;
}
return f(n / pr, p, pr) * rou % p * rem % p;
}
ll g(ll n, ll p) {
if (n < p) return 0;
return g(n / p, p) + n / p;
}
ll C(ll n, ll m, ll p, ll pr) {
ll fz = f(n, p, pr), fm1 = ny(f(m, p, pr), p), fm2 = ny(f(n - m, p, pr), p);
ll pp = ksm(pr, g(n, pr) - g(m, pr) - g(n - m, pr), p);
return fz * fm1 % p * fm2 % p * pp % p;
}
void getprime(int x) {
for (ll i = 2; i * i <= x; ++i) {
if (x % i == 0) {
ll res = 1;
while (x % i == 0) {
res *= i;
x /= i;
}
prime[++cnt] = res;
c[cnt] = C(n, m, res, i);
}
}
if (x != 1) {
prime[++cnt] = x;
c[cnt] = C(n, m, x, x);
}
}
ll exlucas(ll n, ll m, ll p) {
getprime(p);
ll ans = 0;
for (ll i = 1; i <= cnt; ++i) {
ll m = p / prime[i], inv = ny(m, prime[i]);
ans = (ans + c[i] * m % p * inv % p) % p;
}
return ans;
}
void solve() {
cin >> n >> m >> p;
cout << exlucas(n, m, p);
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int _t;
_t = 1;
//cin>>_t;
while (_t--) {
solve();
}
return 0;
}
44.容斥原理
∣ A 1 ∪ A 2 ∪ . . . ∪ A n ∣ = ∑ ∣ A i ∣ − ∑ ∣ A i ∩ A j ∣ + ∑ ∣ A i ∩ A j ∩ A k ∣ + . . . + ( − 1 ) n + 1 ∣ A 1 ∩ A 2 ∩ . . . ∩ A n ∣ |A_1\cup A_2\cup...\cup A_n|=\sum|A_i|-\sum|A_i\cap A_j|+\sum|A_i\cap A_j\cap A_k|+...+(-1)^{n+1}|A_1\cap A_2\cap...\cap A_n| ∣A1∪A2∪...∪An∣=∑∣Ai∣−∑∣Ai∩Aj∣+∑∣Ai∩Aj∩Ak∣+...+(−1)n+1∣A1∩A2∩...∩An∣
45.卡特兰数
C
n
=
1
n
+
1
(
2
n
n
)
C_n=\frac{1}{n+1}\binom{2n}{n}
Cn=n+11(n2n)
C
n
=
(
2
n
n
)
−
(
2
n
n
−
1
)
C_n=\binom{2n}{n}-\binom{2n}{n-1}
Cn=(n2n)−(n−12n)
C
n
=
C
0
C
n
−
1
+
C
1
C
n
−
2
+
.
.
.
+
C
n
−
2
C
1
+
C
n
−
1
C
0
=
∑
C
k
C
n
−
k
,
C
0
=
1
C_n=C_0C_{n-1}+C_1C_{n-2}+...+C_{n-2}C_1+C_{n-1}C_0=\sum C_kC_{n-k},C_0=1
Cn=C0Cn−1+C1Cn−2+...+Cn−2C1+Cn−1C0=∑CkCn−k,C0=1
C
n
=
4
n
−
2
n
+
1
C
n
−
1
,
C
0
=
1
C_n=\frac{4n-2}{n+1}C_{n-1},C_0=1
Cn=n+14n−2Cn−1,C0=1
把n个1和n个0排成一行,使任意前k个数中1的数量总大于等于0的数量的排列数量
应用:
1.棋盘问题(路径问题)
2.括号问题(出栈序列问题)
3.二叉树问题
4.三角剖分问题:n+1条边凸多边形划分三角形,有
C
n
−
1
C_{n-1}
Cn−1种方案
46.第一类斯特林数
s
(
n
,
k
)
,
[
n
k
]
:
s(n,k),{n\brack k}:
s(n,k),[kn]: 把n个不同元素分配到k个圆排列里的方案数
升阶函数各项系数
[
n
k
]
=
[
n
−
1
k
−
1
]
+
(
n
−
1
)
[
n
−
1
k
]
{n\brack k}={n-1\brack k-1}+(n-1){n-1\brack k}
[kn]=[k−1n−1]+(n−1)[kn−1]
s
(
0
,
0
)
=
1
s(0,0)=1
s(0,0)=1
s
(
n
,
0
)
=
0
s(n,0)=0
s(n,0)=0
s
(
n
,
n
)
=
1
s(n,n)=1
s(n,n)=1
s
(
n
,
1
)
=
(
n
−
1
)
!
s(n,1)=(n-1)!
s(n,1)=(n−1)!
s
(
n
,
2
)
=
(
n
−
1
)
!
∑
i
=
1
n
−
1
1
i
s(n,2)=(n-1)!\sum\limits_{i=1}^{n-1}\frac{1}{i}
s(n,2)=(n−1)!i=1∑n−1i1
s
(
n
,
n
−
1
)
=
C
n
2
s(n,n-1)=C_n^2
s(n,n−1)=Cn2
s
(
n
,
n
−
2
)
=
2
C
n
3
+
3
C
n
4
s(n,n-2)=2C_n^3+3C_n^4
s(n,n−2)=2Cn3+3Cn4
∑
k
=
0
n
s
(
n
,
k
)
=
n
!
\sum\limits_{k=0}^ns(n,k)=n!
k=0∑ns(n,k)=n!
47.第二类斯特林数
S
(
n
,
k
)
,
{
n
k
}
:
S(n,k),{n\brace k}:
S(n,k),{kn}: 把n个不同元素拆分成k个集合的方案数
{
n
k
}
=
{
n
−
1
k
−
1
}
+
k
{
n
−
1
k
}
{n\brace k}={n-1\brace k-1}+k{n-1\brace k}
{kn}={k−1n−1}+k{kn−1}
S
(
n
,
m
)
=
1
m
!
∑
k
=
0
m
(
−
1
)
k
(
m
k
)
(
m
−
k
)
n
S(n,m)=\frac{1}{m!}\sum\limits_{k=0}^m(-1)^k\binom{m}{k}(m-k)^n
S(n,m)=m!1k=0∑m(−1)k(km)(m−k)n
S
(
n
,
0
)
=
0
n
S(n,0)=0^n
S(n,0)=0n
S
(
n
,
n
)
=
1
S(n,n)=1
S(n,n)=1
S
(
n
,
1
)
=
1
S(n,1)=1
S(n,1)=1
S
(
n
,
2
)
=
2
n
−
1
−
1
S(n,2)=2^{n-1}-1
S(n,2)=2n−1−1
S
(
n
,
n
−
1
)
=
C
n
2
S(n,n-1)=C_n^2
S(n,n−1)=Cn2
S
(
n
,
n
−
2
)
=
C
n
3
+
3
C
n
4
S(n,n-2)=C_n^3+3C_n^4
S(n,n−2)=Cn3+3Cn4
∑
k
=
0
n
S
(
n
,
k
)
=
B
n
,
B
n
\sum\limits_{k=0}^nS(n,k)=B_n,B_n
k=0∑nS(n,k)=Bn,Bn是贝尔数
∑
k
=
0
n
{
n
k
}
[
k
m
]
=
∑
k
=
0
n
[
n
k
]
{
k
m
}
\sum\limits_{k=0}^n{n\brace k}{k\brack m}=\sum\limits_{k=0}^n{n\brack k}{k\brace m}
k=0∑n{kn}[mk]=k=0∑n[kn]{mk}
48.贝尔数
B
n
B_n
Bn的含义是基数为 n 的集合划分成非空集合的划分数
B
0
=
1
B_0=1
B0=1
B
n
+
1
=
∑
k
=
0
n
(
n
k
)
B
k
B_{n+1}=\sum\limits_{k=0}^n\binom{n}{k}B_k
Bn+1=k=0∑n(kn)Bk
B
n
=
∑
k
=
0
n
S
(
n
,
k
)
B_n=\sum\limits_{k=0}^nS(n,k)
Bn=k=0∑nS(n,k)
第一行第一项为 1
(
a
1
,
1
=
1
)
(a_{1,1}=1)
(a1,1=1);
对于 n>1,第 n 行第一项等于第 n-1 行的第 n - 1 项
(
a
n
,
1
=
a
n
−
1
,
n
−
1
)
(a_{n,1}=a_{n-1,n-1})
(an,1=an−1,n−1);
对于 m,n>1,第 n 行的第 m 项等于它左边和左上角两个数之和
(
a
n
,
m
=
a
n
,
m
−
1
+
a
n
−
1
,
m
−
1
)
(a_{n,m}=a_{n,m-1}+a_{n-1,m-1})
(an,m=an,m−1+an−1,m−1)
每行的首项是贝尔数。
49.错位排列
D
n
=
(
n
−
1
)
(
D
n
−
1
+
D
n
−
2
)
D_n=(n-1)(D_{n-1}+D_{n-2})
Dn=(n−1)(Dn−1+Dn−2)
D
n
=
n
!
∑
k
=
0
n
(
−
1
)
k
k
!
D_n=n!\sum\limits_{k=0}^n\frac{(-1)^k}{k!}
Dn=n!k=0∑nk!(−1)k
50.Burnside定理
置换的合成运算
g
∘
f
(
k
)
=
g
(
f
(
k
)
)
g\circ f(k)=g(f(k))
g∘f(k)=g(f(k))
置换的逆
f
−
1
∘
f
=
τ
f^{-1}\circ f=\tau
f−1∘f=τ,
τ
\tau
τ为恒等置换
设G是S的置换群,f是G中的一个置换,设
λ
(
f
)
\lambda(f)
λ(f)为置换f中不变元的个数,则置换群G的着色方法数为
1
∣
G
∣
∑
f
∈
G
∣
λ
(
f
)
∣
\frac{1}{|G|}\sum\limits_{f\in G}|\lambda(f)|
∣G∣1f∈G∑∣λ(f)∣
P1446 [HNOI2008] Cards
小春现在很清闲,面对书桌上的
n
n
n 张牌,他决定给每张牌染色,目前小春拥有
3
3
3 种颜色:红色,蓝色,绿色。他询问 Sun 有多少种染色方案,Sun 很快就给出了答案。
进一步,小春要求染出
S
r
S_r
Sr 张红色,
S
b
S_b
Sb 张蓝色,
S
g
S_g
Sg 张绿色。他又询问有多少种方案,Sun 想了一下,又给出了正确答案。最后小春发明了
m
m
m 种不同的洗牌法,这里他又问 Sun 有多少种不同的染色方案。两种染色方法相同当且仅当其中一种可以通过任意的洗牌法(即可以使用多种洗牌法,而每种方法可以使用多次)洗成另一种。
Sun 发现这个问题有点难度,决定交给你,由于答案可能很大,你只需要求出答案对于
P
P
P 取模的结果。 保证
P
P
P 为一个质数。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define P pair<int,int>
const double eps = 1e-8;
const ll mod = 1e9 + 7;
const ll INF = 1e18;
const ll N = 1e5 + 10;
int sr, sb, sg, n, m, p, ans;
int a[100], vis[100], siz[100], dp[25][25][25];
ll ksm(ll a, ll b) {
a %= p;
ll res = 1;
while (b) {
if (b & 1) res = res * a % p;
a = a * a % p;
b >>= 1;
}
return res;
}
int calc() {
memset(dp, 0, sizeof(dp));
memset(vis, 0, sizeof(vis));
int cnt = 0;
for (int i = 1; i <= n; ++i) {
if (!vis[i]) {
int p = i, len = 0;
while (!vis[p]) {
len++;
vis[p] = 1;
p = a[p];
}
siz[++cnt] = len;
}
}
dp[0][0][0] = 1;
for (int i = 1; i <= cnt; ++i) {
for (int r = sr; r>=0; --r) {
for (int b = sb; b>=0; --b) {
for (int g = sg; g>=0; --g) {
if (r >= siz[i]) dp[r][b][g] = (dp[r][b][g] + dp[r - siz[i]][b][g]) % p;
if (b >= siz[i]) dp[r][b][g] = (dp[r][b][g] + dp[r][b - siz[i]][g]) % p;
if (g >= siz[i]) dp[r][b][g] = (dp[r][b][g] + dp[r][b][g - siz[i]]) % p;
}
}
}
}
return dp[sr][sb][sg];
}
void solve() {
cin >> sr >> sb >> sg >> m >> p;
n = sr + sb + sg;
for (int i = 1; i <= m; ++i) {
for (int j = 1; j <= n; ++j) {
cin >> a[j];
}
ans = (ans + calc()) % p;
}
for (int i = 1; i <= n; ++i) a[i] = i;
ans = (ans + calc()) % p;
cout << ans * ksm(m + 1, p - 2) % p;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int _t;
_t = 1;
//cin>>_t;
while (_t--) {
solve();
}
return 0;
}
51.Pólya 定理
任何一种置换都能表示成若干互不相交的循环的乘积。
在一种置换中,用
λ
i
(
f
)
\lambda_i(f)
λi(f) 表示长度为 i 的循环的个数,用
λ
(
f
)
\lambda(f)
λ(f)表示
f
∈
G
f\in G
f∈G中循环的个数。
设
G
=
{
f
1
,
f
2
,
.
.
.
,
f
n
}
G=\{f_1,f_2,...,f_n\}
G={f1,f2,...,fn}是
S
n
S_n
Sn是置换群,用 m 种颜色对 n 个对象着色,G的着色方法数为
1
∣
G
∣
∑
f
∈
G
m
λ
(
f
)
=
1
∣
G
∣
∑
f
∈
G
m
λ
1
(
f
)
+
λ
2
(
f
)
+
.
.
.
+
λ
n
(
f
)
\frac{1}{|G|}\sum\limits_{f\in G}m^{\lambda(f)}=\frac{1}{|G|}\sum\limits_{f\in G}m^{\lambda_1(f)+\lambda_2(f)+...+\lambda_n(f)}
∣G∣1f∈G∑mλ(f)=∣G∣1f∈G∑mλ1(f)+λ2(f)+...+λn(f)
第 i 个旋转的循环节数为
g
c
d
(
n
,
i
)
,
0
≤
i
<
n
gcd(n,i),0\le i<n
gcd(n,i),0≤i<n
若 n 为偶数,经过多边形顶点的对称轴有n/2个,即n/2种置换,每种置换方案有n/2+1个循环节;不经过顶点的对称轴有n/2个,每种置换方案有n/2个循环节
若 n 为奇数,对称轴都经过顶点,有n种置换,每种置换有n/2+1个循环节
P4980 【模板】Pólya 定理
给定一个
n
n
n 个点,
n
n
n 条边的环,有
n
n
n 种颜色,给每个顶点染色,问有多少种本质不同的染色方案,答案对
1
0
9
+
7
10^9+7
109+7 取模
注意本题的本质不同,定义为:只需要不能通过旋转与别的染色方案相同。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define P pair<int,int>
const double eps = 1e-8;
const ll mod = 1e9 + 7;
const ll INF = 1e18;
const ll N = 1e5 + 10;
ll getphi(ll n) {
ll res = n;
for (ll i = 2; i*i<=n; ++i) {
if (n % i == 0) {
res = res / i * (i - 1);
while (n % i == 0) n /= i;
}
}
if (n > 1) res = res / n * (n - 1);
return res;
}
ll ksm(ll a, ll b) {
a %= mod;
ll res = 1;
while (b) {
if (b & 1) res = (res * a) % mod;
a = (a * a) % mod;
b >>= 1;
}
return res;
}
void solve() {
ll n,ans=0;
cin >> n;
for (ll i = 1; i * i <= n; ++i) {
if (n % i == 0) {
ans = (ans + getphi(i) * ksm(n, n / i - 1)) % mod;
if (i * i != n) ans = (ans + getphi(n / i) * ksm(n, i - 1)) % mod;
}
}
cout << ans << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int _t;
//_t = 1;
cin>>_t;
while (_t--) {
solve();
}
return 0;
}
52.生成函数
生成函数的定义:
g
(
x
)
=
a
0
+
a
1
x
+
a
2
x
2
+
a
3
x
3
+
.
.
.
g(x)=a_0+a_1x+a_2x^2+a_3x^3+...
g(x)=a0+a1x+a2x2+a3x3+....称g(x)是序列
a
0
,
a
1
,
a
2
,
.
.
.
a_0,a_1,a_2,...
a0,a1,a2,...的生成函数,x 充当形式参数没有意义。
普通生成函数:k 种元素的多重集合的 r 组合数(有限和无限多重集都可以)
指数生成函数:k 种元素的多重集合的 r 排列数(有限和无限多重集都可以)
列出式子,有 n 项,每项代表一个种类的限制(一个括号代表一项),共取 m 个,生成函数为 g ( x ) = ( 1 + x + x 2 + … ) ( 1 + x 2 + x 4 + … ) ( 1 + x 3 + x 6 + … ) … g(x)=(1+x+x^2+\dots)(1+x^2+x^4+\dots)(1+x^3+x^6+\dots)\dots g(x)=(1+x+x2+…)(1+x2+x4+…)(1+x3+x6+…)…计算 x m x^m xm 的系数,化为分式,二项式展开。
- HDU - 2152 Fruit (普通生成函数)
有 n 种水果,每种水果出现的次数在 [ a i , b i ] [a_i,b_i] [ai,bi] 之间,问一共买 m 个水果有多少种购买方案?
( x a 1 + ⋯ + x b 1 ) ( x a 2 + ⋯ + x b 2 ) … ( x a n + ⋯ + x b n ) (x^{a_1}+\dots+x^{b_1})(x^{a_2}+\dots+x^{b_2})\dots(x^{a_n}+\dots+x^{b_n}) (xa1+⋯+xb1)(xa2+⋯+xb2)…(xan+⋯+xbn)对这个生成函数求 x m x^m xm 的系数 - HDU - 1521 排列组合 (指数生成函数)
有 n 种物品,每种物品的数量为 a i a_i ai ,问取 m 个物品的排列数
( 1 + x + x 2 2 ! + ⋯ + x a 1 a 1 ! ) ( 1 + x + x 2 2 ! + ⋯ + x a 2 a 2 ! ) … ( 1 + x + x 2 2 ! + ⋯ + x a n a n ! ) (1+x+\frac {x^2}{2!}+\dots+\frac {x^{a_1}}{a_1!})(1+x+\frac {x^2}{2!}+\dots+\frac {x^{a_2}}{a_2!})\dots(1+x+\frac {x^2}{2!}+\dots+\frac {x^{a_n}}{a_n!}) (1+x+2!x2+⋯+a1!xa1)(1+x+2!x2+⋯+a2!xa2)…(1+x+2!x2+⋯+an!xan)对这个式子求 x m x^m xm 的系数 - HDU - 1028 Ignatius and the Princess III (普通生成函数)
将整数 n 拆分成正整数相加的形式,问有几种组合
每一个小于 n 的正整数都有可能组成。共有 n 个因子,可以得到生成函数
g ( x ) = ( 1 + x + x 2 + … ) ( 1 + x 2 + x 4 + … ) ( 1 + x 3 + x 6 + … ) … g(x)=(1+x+x^2+\dots)(1+x^2+x^4+\dots)(1+x^3+x^6+\dots)\dots g(x)=(1+x+x2+…)(1+x2+x4+…)(1+x3+x6+…)…
x n x^n xn的系数就是所求的组合方式 - HDU - 1085 Holding Bin-Laden Captive! (普通生成函数)
由面值为1、2、5的硬币不能组成的最小面值是多少。
可以得到生成函数 g ( x ) = ( 1 + x + x 2 + … ) ( 1 + x 2 + x 4 + … ) ( 1 + x 5 + x 10 + … ) g(x)=(1+x+x^2+\dots)(1+x^2+x^4+\dots)(1+x^5+x^{10}+\dots) g(x)=(1+x+x2+…)(1+x2+x4+…)(1+x5+x10+…)从小到大遍历系数,为 0 的那一项就是不能组成的 - HDU - 2189 悼念512汶川大地震遇难同胞——来生一起走 (普通生成函数)
把n个人分成几个小组。每个小组的人数必须是素数
用素数组成n,但是不知道用具体多少个素数。每一个小于等于 n 的素数都是一个因子。可以得到生成函数: g ( x ) = ( 1 + x 2 + x 4 + x 6 + … ) ( 1 + x 3 + x 6 + … ) ( 1 + x 5 + x 10 + … ) x n g(x)=(1+x^2+x^4+x^6+\dots)(1+x^3+x^{6}+\dots)(1+x^5+x^{10}+\dots)\ x^n g(x)=(1+x2+x4+x6+…)(1+x3+x6+…)(1+x5+x10+…) xn的系数就是这个组合数的答案 - BZOJ3028: 食物(普通生成函数 + 推导 + 欧拉降幂)
明明这次又要出去旅游了,和上次不同的是,他这次要去宇宙探险!我们暂且不讨论他有多么NC,他又幻想了他应该带一些什么东西。理所当然的,你当然要帮他计算携带N件物品的方案数。他这次又准备带一些受欢迎的食物,如:蜜桃多啦,鸡块啦,承德汉堡等等。每种食物的限制如下:
(1)承德汉堡:偶数个(2)可乐:0个或1个(3)鸡腿:0个,1个或2个(4)蜜桃多:奇数个(5)鸡块:4的倍数个(6)包子:0个,1个,2个或3个(7)土豆片炒肉:不超过一个。(8)面包:3的倍数个
很明显示生成函数的题,并且考虑的是组合数,可以得到生成函数:思路:很明显示生成函数的题,并且考虑的是组合数
可以得到生成函数: g ( x ) = ( 1 + x 2 + … ) ( 1 + x ) ( 1 + x + x 2 ) ( x + x 3 + … ) ( 1 + x 4 + x 8 + … ) ( 1 + x + x 2 + x 3 ) ( 1 + x ) ( 1 + x 3 + x 6 + … ) = 1 1 − x 2 ( 1 + x ) 1 − x 3 1 − x x 1 1 − x 2 1 1 − x 4 ( 1 − x 4 1 − x ) ( 1 + x ) 1 1 − x 3 = x ( 1 − x ) 4 = ∑ n = 0 ∞ C n + 4 − 1 3 x n + 1 = ∑ n = 0 ∞ C n + 2 3 x n g(x)=(1+x^2+\dots )(1+x)(1+x+x^2)(x+x^3+\dots)(1+x^4+x^8+\dots)(1+x+x^2+x^3)(1+x)(1+x^3+x^6+\dots)=\frac 1{1-x^2}(1+x)\frac {1-x^3}{1-x}x\frac 1{1-x^2}{\frac {1}{1-x^4}}(\frac {1-x^4}{1-x})(1+x)\frac 1{1-x^3}=\frac x{(1-x)^4}=\sum_{n=0}^{\infty}C_{n+4-1}^3x^{n+1}=\sum_{n=0}^{\infty}C_{n+2}^3x^n g(x)=(1+x2+…)(1+x)(1+x+x2)(x+x3+…)(1+x4+x8+…)(1+x+x2+x3)(1+x)(1+x3+x6+…)=1−x21(1+x)1−x1−x3x1−x211−x41(1−x1−x4)(1+x)1−x31=(1−x)4x=∑n=0∞Cn+4−13xn+1=∑n=0∞Cn+23xn
因此,答案就是 a n = C n + 2 3 a_n=C_{n+2}^3 an=Cn+23 - 2019 ICPC上海网络赛 E. Counting Sequences II (指数生成函数 + 推导)
给定n个位置,对于每一个数 a i a_i ai,都满足 1 ≤ a i ≤ m 1 ≤ a_i ≤ m 1≤ai≤m,并且如果 a i a_i ai是偶数的话, a i a_i ai出现的次数是偶数次,求排列的个数
求的是排列,所以是指数型生成函数。在[1,m]的范围内,相当于有m个因子,每个因子分别代表对1 ,2 ,3 ,4 , … ,m各自的限制。其中偶数有 f l o o r ( m 2 ) floor(\frac m2) floor(2m) 个,奇数有 m − f l o o r ( m 2 ) m-floor(\frac m2) m−floor(2m) ,设 t = f l o o r ( m 2 ) t=floor(\frac m2) t=floor(2m) ,可以得到指数型生成函数
g ( x ) = ( 1 + x + x 2 2 ! + x 3 3 ! + … ) m − t ( 1 + x 2 2 ! + x 4 4 ! + … ) t g(x)=(1+x+\frac {x^2}{2!}+\frac {x^3}{3!}+\dots)^{m-t}(1+\frac {x^2}{2!}+\frac {x^4}{4!}+\dots)^{t} g(x)=(1+x+2!x2+3!x3+…)m−t(1+2!x2+4!x4+…)t
化简可得: g ( x ) = e x ( m − t ) ( e x + e − x 2 ) t = e x ( m − t ) ∑ i = 0 t C t i e x ( t − i ) e − x i 2 t = ∑ i = 0 t C t i e ( m − 2 i ) x 2 t = ∑ n = 0 ∑ i = 0 t C t i ( m − 2 i ) n 2 t x n n ! g(x)=e^{x(m-t)}(\frac {e^x+e^{-x}}2)^t=\frac{e^{x(m-t)}\sum_{i=0}^tC_t^ie^{x(t-i)}e^{-xi}}{2^t}=\frac {\sum_{i=0}^tC_t^ie^{(m-2i)x}}{2^t}=\sum\limits_{n=0}\frac{\sum_{i=0}^tC_t^i{(m-2i)^n}}{2^t}\frac {x^n}{n!} g(x)=ex(m−t)(2ex+e−x)t=2tex(m−t)∑i=0tCtiex(t−i)e−xi=2t∑i=0tCtie(m−2i)x=n=0∑2t∑i=0tCti(m−2i)nn!xn
因此可以得到 a n = ∑ i = 0 t C t i ( m − 2 i ) n 2 t a_n=\frac{\sum_{i=0}^tC_t^i{(m-2i)^n}}{2^t} an=2t∑i=0tCti(m−2i)n , a n a_n an 就是答案 - 硬币兑换问题
将n元钱全部兑换成1元和2元,有多少种兑换方法
g ( x ) = ( 1 + x + x 2 + … ) ( 1 + x 2 + x 4 + … ) g(x)=(1+x+x^2+\dots)(1+x^2+x^4+\dots) g(x)=(1+x+x2+…)(1+x2+x4+…) - 组合选取问题
一副三色纸牌共 32 张其中红黄蓝每种颜色的牌各 10 张编号分别是1,2,3,…,10;另有大小王各一张,编号均为 0 。从这副牌中取出若干张牌,然后按如下规则计算分值:每张编号为 k 的牌计为 2 k 2^k 2k 分若它们的分值之和等于 2004 就称这些牌为一个“好”牌组。试求“好”牌组的个数
g ( x ) = ( 1 + x 2 0 ) 2 ( 1 + x 2 1 ) 3 … ( 1 + x 2 10 ) 3 g(x)=(1+x^{2^0})^2(1+x^{2^1})^3\dots(1+x^{2^{10}})^3 g(x)=(1+x20)2(1+x21)3…(1+x210)3 - 有 n 个标号为 1 , 2 , ⋯ , n 的球,放到 m 个无标号盒子 (盒内顺序有标号),且每个盒子球数不超过 k ,求方案数对 998244353 取模。
考虑每个盒子内球的生成函数 ∑ i = 1 k x i \sum\limits_{i = 1} ^ {k}x ^ i i=1∑kxi ,那么 m 个盒子的生成函数就为 ( ∑ i = 1 k x i ) m \left( \sum\limits_{i = 1} ^ {k}x ^ i\right) ^ m (i=1∑kxi)m,那么方案数就为第 n 项系数。由于球带标号,所以需要对答案全排列,也就是乘 n! ,又由于盒子不带标号,所以要对答案除 m! ,那么答案为 n ! m ! × [ x n ] ( ∑ i = 1 k x i ) m \frac{n!}{m!} \times [x ^ n]\left( \sum\limits_{i = 1} ^ {k}x ^ i\right) ^ m m!n!×[xn](i=1∑kxi)m
1
(
1
−
x
)
n
=
∑
k
=
0
∞
C
n
+
k
−
1
k
x
k
\frac{1}{(1-x)^n}=\sum\limits_{k=0}^\infty C_{n+k-1}^kx^k
(1−x)n1=k=0∑∞Cn+k−1kxk
1
1
−
x
=
1
+
x
+
x
2
+
x
3
+
.
.
.
\frac{1}{1-x}=1+x+x^2+x^3+...
1−x1=1+x+x2+x3+...
1
1
−
x
6
=
1
+
x
6
+
x
12
+
.
.
.
\frac{1}{1-x^6}=1+x^6+x^{12}+...
1−x61=1+x6+x12+...
1
−
x
6
1
−
x
=
1
+
x
+
x
2
+
x
3
+
x
4
+
x
5
\frac{1-x^6}{1-x}=1+x+x^2+x^3+x^4+x^5
1−x1−x6=1+x+x2+x3+x4+x5
x
(
1
−
x
)
2
=
x
+
2
x
2
+
3
x
3
+
.
.
.
\frac{x}{(1-x)^2}=x+2x^2+3x^3+...
(1−x)2x=x+2x2+3x3+...
e
x
=
1
+
1
1
!
x
+
1
2
!
x
2
+
1
3
!
x
3
+
.
.
.
e^x=1+\frac{1}{1!}x+\frac{1}{2!}x^2+\frac{1}{3!}x^3+...
ex=1+1!1x+2!1x2+3!1x3+...
e
2
x
=
1
+
1
1
!
2
x
+
1
2
!
(
2
x
)
2
+
1
3
!
(
2
x
)
3
+
.
.
.
e^{2x}=1+\frac{1}{1!}2x+\frac{1}{2!}(2x)^2+\frac{1}{3!}(2x)^3+...
e2x=1+1!12x+2!1(2x)2+3!1(2x)3+...
53.康托展开
P5367 【模板】康托展开
对于一个1到n的排列{a1 ,a2 ,⋯,an },比它小的排列有
∑
i
=
1
n
s
u
m
a
i
×
(
n
−
i
)
!
\sum\limits_{i=1}^nsum_{a_i}\times(n-i)!
i=1∑nsumai×(n−i)!个
s
u
m
a
i
sum_{a_i}
sumai为在 ai 后面的元素里比它小的元素个数,即
∑
j
=
1
n
(
a
j
<
a
i
)
\sum\limits_{j=1}^n(a_j<a_i)
j=1∑n(aj<ai),树状数组求解
逆康托展开
类似于进制转换,不断 %(n-i)! ,/(n-i)!就可以得到sum数组,然后就可以还原出原排列。
54.巴什博弈
HDU - 1846 Brave Game
各位勇敢者要玩的第一个游戏是什么呢?很简单,它是这样定义的:
1、 本游戏是一个二人游戏;
2、 有一堆石子一共有n个;
3、 两人轮流进行;
4、 每走一步可以取走1…m个石子;
5、 最先取光石子的一方为胜;
如果游戏的双方使用的都是最优策略,请输出哪个人能赢。
结论:被(m+1)整除,先手败
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define P pair<int,int>
const double eps = 1e-8;
const ll INF = 1e18;
const ll N = 1e5 + 10;
void solve() {
int n;
cin >> n;
for (int i = 1; i <= n; ++i) {
int x, y;
cin >> x >> y;
if (x % (y + 1)==0) cout << "second\n";
else cout << "first\n";
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int _t;
_t = 1;
//cin >> _t;
while (_t--) {
solve();
}
return 0;
}
55.nim博弈
P2197 【模板】nim 游戏
甲,乙两个人玩 nim 取石子游戏。
nim 游戏的规则是这样的:地上有
n
n
n 堆石子(每堆石子数量小于
1
0
4
10^4
104),每人每次可从任意一堆石子里取出任意多枚石子扔掉,可以取完,不能不取。每次只能从一堆里取。最后没石子可取的人就输了。假如甲是先手,且告诉你这
n
n
n 堆石子的数量,他想知道是否存在先手必胜的策略。
结论:异或和为0,先手败
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define P pair<int,int>
const double eps = 1e-8;
const ll INF = 1e18;
const ll N = 1e5 + 10;
void solve() {
int ans = 0, n;
cin >> n;
for (int i = 1; i <= n; ++i) {
int a;
cin >> a;
ans ^= a;
}
if (ans) cout << "Yes\n";
else cout << "No\n";
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int _t;
//_t = 1;
cin>>_t;
while (_t--) {
solve();
}
return 0;
}
56.阶梯博弈
AcWing 892. 台阶-Nim游戏
现在,有一个n级台阶的楼梯,每级台阶上都有若干个石子,其中第i级台阶上有
a
i
a_i
ai个石子(i≥1)。
两位玩家轮流操作,每次操作可以从任意一级台阶上拿若干个石子放到下一级台阶中(不能不拿)。
已经拿到地面上的石子不能再拿,最后无法进行操作的人视为失败。
问如果两人都采用最优策略,先手是否必胜。
结论:当奇数级阶梯的石子数量异或和为0,先手必败,否则先手必胜。
57.威佐夫博弈
P2252 [SHOI2002]取石子游戏|【模板】威佐夫博弈
有两堆石子,数量任意,可以不同。游戏开始由两个人轮流取石子。游戏规定,每次有两种不同的取法,一是可以在任意的一堆中取走任意多的石子;二是可以在两堆中同时取走相同数量的石子。最后把石子全部取完者为胜者。现在给出初始的两堆石子的数目,你先取,假设双方都采取最好的策略,问最后你是胜者还是败者。
结论:等于(b-a)*1.618,先手败
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define P pair<int,int>
const double eps = 1e-8;
const ll INF = 1e18;
const ll N = 1e5 + 10;
void solve() {
int a, b;
cin >> a >> b;
double temp = (sqrt(5) + 1) / 2;
if (a > b) swap(a, b);
if (a == int((b - a) * temp)) cout << 0;
else cout << 1;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int _t;
_t = 1;
//cin >> _t;
while (_t--) {
solve();
}
return 0;
}
58.斐波那契博弈
有一堆个数为n(n>=2)的石子,游戏双方轮流取石子,规则如下:
1)先手不能在第一次把所有的石子取完,至少取1颗;
2)之后每次可以取的石子数至少为1,至多为对手刚取的石子数的2倍。
约定取走最后一个石子的人为赢家,求必败态。
结论:当n为斐波那契数时,先手必败。
59.P-position与N-positon
P-position为前一个玩家的必胜位置,N-position为下一个玩家的必胜位置
60.图游戏与SG函数
sg(x)等于x所有后缀sg值中未出现过的最小非负整数
SG函数实现巴什博弈,sg(x)=0的点是必败点
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define P pair<int,int>
const double eps = 1e-8;
const ll mod = 1e9 + 7;
const ll INF = 1e18;
const ll N = 1e3 + 10;
int n, m, sg[N], s[N];
void getsg() {
memset(sg, 0, sizeof(sg));
for (int i = 1; i <= n; ++i) {
memset(s, 0, sizeof(s));
for (int j = 1; j <= m && i >= j; ++j) s[sg[i - j]] = 1;
for (int j = 0; j <= n; ++j) {
if (!s[j]) {
sg[i] = j;
break;
}
}
}
}
void solve() {
cin >> n >> m;
getsg();
if (sg[n]) cout << "first\n";
else cout << "second\n";
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int _t;
//_t = 1;
cin>>_t;
while (_t--) {
solve();
}
return 0;
}