信息学竞赛CSP中组合数学知识进阶及经典题目

组合数学

组合数卷积(范德蒙德卷积)

∑ i = 0 k ( n i ) ( m k − i ) = ( n + m k ) \sum_{i=0}\limits^k\dbinom{n}{i}\dbinom{m}{k-i}=\dbinom{n+m}{k} i=0k(in)(kim)=(kn+m)

组合意义:有 n n n 个红球以及 m m m 蓝球,选出 i i i 个红球,再选出 k − i k-i ki 个蓝球,等价于 n n n 个红球和 m m m 个蓝球,任意选出 k k k 个球
∑ i = 0 k ( i n ) ( k − i m ) = ( k + 1 n + m + 1 ) \sum_{i=0}^k\limits\dbinom{i}{n}\dbinom{k-i}{m}=\dbinom{k+1}{n+m+1} i=0k(ni)(mki)=(n+m+1k+1)
组合意义:有 k + 1 k + 1 k+1 个球排成一排,先选择一个球涂成绿色,然后在绿球的左边的所有球中选出 n n n 个涂成红色,绿色右边的所有球中选出 m m m 个涂成蓝色,等价于 k + 1 k+1 k+1 个球排成一排,选出 n + m + 1 n+m+1 n+m+1 个球。

组合数前缀和

( 0 m ) + ( 1 m ) + ⋯ + ( n m ) = ( n + 1 m + 1 ) \dbinom{0}{m}+\dbinom{1}{m}+\dots+\dbinom{n}{m}=\dbinom{n+1}{m+1} (m0)+(m1)++(mn)=(m+1n+1)

组合意义:对于 n + 1 n+1 n+1 个球,选择其中一个涂红,再在红球的左边选择 m m m 个涂蓝,等价于 n + 1 n+1 n+1 个球中,选择 m + 1 m+1 m+1
f ( n , m ) = ( n 0 ) + ( n 1 ) + ⋯ + ( n m ) f(n,m)=\binom{n}{0}+\binom{n}{1}+\dots+\binom{n}{m} f(n,m)=(0n)+(1n)++(mn)
无法快速计算 f ( n , m ) f(n,m) f(n,m),但是可以 O ( n ) O(n) O(n) 算出 f ( 0 ∼ n , m ) f(0\sim n,m) f(0n,m)

计算方式:

f ( n , m ) = ∑ i = 0 m ( n i ) = ∑ i = 0 m ( ( n − 1 i ) + ( n − 1 i − 1 ) ) = ∑ i = 0 m ( n − 1 i ) + ∑ i = 0 m − 1 ( n − 1 i ) = 2 ∑ i = 0 m ( n − 1 i ) − ( n − 1 m ) = 2 f ( n − 1 , m ) − ( n − 1 m ) \begin{align*} &f(n,m)&=&\sum\limits_{i=0}^m\dbinom{n}{i}\\ &&=&\sum_{i=0}^m(\dbinom{n-1}{i}+\dbinom{n-1}{i-1}) \\ &&=&\sum_{i=0}^m\dbinom{n-1}{i}+\sum_{i=0}^{m-1}\dbinom{n-1}{i}\\ &&=&2\sum_{i=0}^m\dbinom{n-1}{i}-\dbinom{n-1}{m}\\ &&=&2f(n-1,m)-\dbinom{n-1}{m} \end{align*} f(n,m)=====i=0m(in)i=0m((in1)+(i1n1))i=0m(in1)+i=0m1(in1)2i=0m(in1)(mn1)2f(n1,m)(mn1)

其中 ② ② 式为组合恒等式: ( n m ) = ( n − 1 m ) + ( n − 1 m − 1 ) \dbinom{n}{m}=\dbinom{n-1}{m}+\dbinom{n-1}{m-1} (mn)=(mn1)+(m1n1)

其中 ③ ③ 式后项的 i i i 并非上式的 i i i,而是 i − 1 i-1 i1,所以上界的范围是到 m − 1 m-1 m1


C F 1327 E − C o u n t   T h e   B l o c k s \mathrm{CF1327E - Count\ The\ Blocks} CF1327ECount The Blocks

D e s c r i p t i o n \mathrm{Description} Description

给定正整数 n n n

对于一个数 t t t,定义一个块是 t t t 中的连续相等的一串数字。写下 0 ⋯ 1 0 n − 1 0\cdots10^n-1 010n1 的所有数,不足 n n n 位的用前导 0 0 0 补足 n n n 位。

对于每个 i = 1 ⋯ n i=1\cdots n i=1n,求这 1 0 n 10^n 10n 个数中一共有多少个长为 i i i 的块。

S o l u t i o n \mathrm{Solution} Solution

分类讨论:

  • i = n i=n i=n 时,答案为 10 10 10,即全部相同,只有 10 10 10 种选法
  • i < n i<n i<n
    • 当长度为 i i i 的连通块位于两边时: 10 × 9 × 1 0 n − i − 1 10\times 9\times 10^{n-i-1} 10×9×10ni1
    • 当长度为 i i i 的连通块位于中间时: 10 × 9 2 × 1 0 max ⁡ ( 0 , n − i − 2 ) 10\times9^2\times 10^{\max(0,n-i-2)} 10×92×10max(0,ni2)
A c c e p t e d   C o d e \mathrm{Accepted\ Code} Accepted Code
#include <bits/stdc++.h>
#define int long long

using namespace std;

typedef pair<int, int> PII;
typedef long long LL;

const int MOD = 998244353;

int ksm(int a, int b, int p)
{
	int Result = 1;
	while (b)
	{
		if (b & 1) Result = Result * a % p;
		a = a * a % p;
		b >>= 1;
	}

	return Result;
}

signed main()
{
	cin.tie(0);
	cout.tie(0);
	ios::sync_with_stdio(0);

	int N;

	cin >> N;

	for (int i = 1; i <= N; i ++)
		if (i == N) cout << 10 << " ";
		else if (i == N - 1) cout << 10 * 9 * ksm(10, N - i - 1, MOD) * 2 % MOD << " ";
		else cout << (10 * 9 * ksm(10, N - i - 1, MOD) * 2 % MOD + (N - i - 1) * 10 * 9 * 9 % MOD * ksm(10, max(0ll, N - i - 2), MOD) % MOD) % MOD << " ";

	return 0;
}

C F 1761 D − C a r r y   B i t \mathrm{CF1761D - Carry\ Bit} CF1761DCarry Bit

D e s c r i p t i o n \mathrm{Description} Description

定义 f ( a , b ) f(a,b) f(a,b) 表示二进制下计算 a + b a+b a+b 的进位次数。

给定 n , k n,k n,k,计算共有多少二元组 ( a , b ) ( 0 ≤ a , b ≤ 2 n ) (a,b)(0\le a,b\le 2^n) (a,b)(0a,b2n) 满足 f ( a , b ) = k f(a,b)=k f(a,b)=k

0 ≤ k < n ≤ 1 0 6 0\le k<n\le 10^6 0k<n106

S o l u t i o n \mathrm{Solution} Solution

首先,考虑一个暴力 DP:设 f i , j , 0 / 1 f_{i,j,0/1} fi,j,0/1 表示对于前 i i i 位,总共进位次数为 j j j,当前是否进位。

则有:
f i + 1 , j , 0 = 3 f i , j , 0 + f i , j , 1 f i + 1 , j + 1 , 1 = f i , j , 0 + 3 f i , j , 1 \begin{align*} f_{i+1,j,0}&=3f_{i,j,0}+f_{i,j,1}\\ f_{i+1,j+1,1}&=f_{i,j,0}+3f_{i,j,1} \end{align*} fi+1,j,0fi+1,j+1,1=3fi,j,0+fi,j,1=fi,j,0+3fi,j,1
那么,若当前用 x x x 表示是否进位,则有 3 3 3 的贡献保持不变, 1 1 1 的贡献发生翻转,转移路线如图:

请添加图片描述

如图所示,斜边表示翻转,横边表示保持原有状态,那么转移其实就是这上面的一条条路径。(第一行为不进位,第二行为进位)

若对于一条 01 01 01 路径,进位的次数是多少呢?其实是路径上 1 1 1 的个数。

那么,考虑枚举路径上 01 01 01 翻转的次数,记作 i i i。则路径上 1 1 1 的极大连通块的数量 ⌈ i 2 ⌉ \lceil \frac{i}{2}\rceil 2i,由于总共分成的连通块数量为 i + 1 i+1 i+1,所以 0 0 0 的极大连通块数量为 i + 1 − ⌈ i 2 ⌉ i+1-\lceil\frac{i}{2}\rceil i+12i。那么,由于需要 1 1 1 的个数为 K K K,那么加上初始状态 0 0 0(没有进位),总共 n + 1 n+1 n+1 个状态,这其中有 K K K 个为 1 1 1,那么就有 n + 1 − K n+1-K n+1K 个为 0 0 0

综上所述, 01 01 01 路径的数量应为将 K K K 1 1 1 划分成 ⌈ i 2 ⌉ \lceil \frac{i}{2}\rceil 2i 段的数量 n + 1 − K n+1-K n+1K 0 0 0 划分成 i + 1 − ⌈ i 2 ⌉ i+1-\lceil\frac{i}{2}\rceil i+12i 段的数量,通过隔板法可得: ( n − K i − ⌈ i 2 ⌉ ) ( K − 1 ⌈ i 2 ⌉ ) \dbinom{n-K}{i-\lceil\frac{i}{2}\rceil}\dbinom{K-1}{\lceil \frac{i}{2}\rceil} (i2inK)(2iK1)

不过,不要忘记对于保持不变,还有 3 3 3 的转移系数,所以还要乘 3 n − i 3^{n-i} 3ni

最终式子:
∑ i = 0 n 3 n − i ( n − K i − ⌈ i 2 ⌉ ) ( K − 1 ⌈ i 2 ⌉ ) \sum_{i=0}^n 3^{n-i}\dbinom{n-K}{i-\lceil\frac{i}{2}\rceil}\dbinom{K-1}{\lceil \frac{i}{2}\rceil} i=0n3ni(i2inK)(2iK1)

A c c e p t e d   C o d e \mathrm{Accepted\ Code} Accepted Code
#include <bits/stdc++.h>
#define int long long

using namespace std;

typedef pair<int, int> PII;
typedef long long LL;

const int MAXN = 1e6 + 10, MOD = 1e9 + 7;

int N, K;
int inv[MAXN], fact[MAXN], infact[MAXN], P3[MAXN];

int C(int n, int m) { return m > n ? 0 : fact[n] * infact[m] % MOD * infact[n - m] % MOD; }

signed main()
{
	cin.tie(0);
	cout.tie(0);
	ios::sync_with_stdio(0);

	cin >> N >> K;

	inv[1] = 1;
	for (int i = 2; i <= N; i ++)
		inv[i] = (MOD - MOD / i) * inv[MOD % i] % MOD;
	fact[0] = infact[0] = P3[0] = 1;
	for (int i = 1; i <= N; i ++)
		fact[i] = fact[i - 1] * i % MOD, infact[i] = infact[i - 1] * inv[i] % MOD, P3[i] = P3[i - 1] * 3 % MOD;

	if (!K)
	{
		cout << P3[N] << endl;
		return 0;
	}

	int res = 0;
	for (int i = 0; i <= N; i ++)
		res = (res + P3[N - i] * C(N - K, i + 1 - (i + 1) / 2 - 1) % MOD * C(K - 1, (i + 1) / 2 - 1) % MOD) % MOD;

	cout << res << endl;

	return 0;
}

[ A B C 134 F ] P e r m u t a t i o n   O d d n e s s \mathrm{[ABC134F] Permutation\ Oddness} [ABC134F]Permutation Oddness

D e s c r i p t i o n \mathrm{Description} Description

定义一个 1 ∼ n 1 \sim n 1n 的排列 p p p 的「怪异度」为
∑ i = 1 n ∣ p i − i ∣ \sum_{i=1}^n|p_i-i| i=1npii
求「怪异度」为 k k k 1 ∼ n 1 \sim n 1n 的排列数,答案对 1 0 9 + 7 10^9+7 109+7 取模。


S o l u t i o n \mathrm{Solution} Solution

对于排列计数的问题,最为困难的地方在于如何去一一对应的计算排列数。

对于本题,将题目形式化为有 n n n 只老鼠,以及 n n n 个洞,老鼠去找洞(一个洞只能留一只老鼠),最终的「怪异度」为老鼠走过的距离,即每条边走过的次数之和。

那么,考虑对于位置 i i i,老鼠 i i i 可以去找 < i <i <i 的洞,也可以找 > i >i >i 的洞(洞也是类似的)。不过,如果一只老鼠找 > i >i >i 的洞,那么也必有一个 > i >i >i 的老鼠找 < i <i <i 的洞,即是选择是对称的。

所以,不妨设 f i , j , k f_{i,j,k} fi,j,k 表示前 i i i 只老鼠,有 j j j 只老鼠(也是 j j j 个洞,因为是对称的)是去找 > i >i >i 的位置的,目前的和为 k k k

f i , j , k f_{i,j,k} fi,j,k 转移到 i + 1 i+1 i+1 的时候,那么 i ↔ i + 1 i\leftrightarrow i+1 ii+1 这条边会被经过 2 j 2j 2j 次,因为 j j j ≤ i \le i i 老鼠取 > i >i >i 的位置, j j j 个洞会有 j j j > i >i >i 的老鼠所找到,所以会被经过 2 j 2j 2j 次。

那么,接下来分类讨论 j j j 的变化:

  • i + 1 i+1 i+1 号老鼠和洞都是向 > i + 1 >i+1 >i+1 的位置匹配的,那么 f i , j , k → f i + 1 , j + 1 , k + 2 j f_{i,j,k}\rightarrow f_{i+1,j+1,k+2j} fi,j,kfi+1,j+1,k+2j,因为会多一对。
  • i + 1 i+1 i+1 号老鼠和洞有一个向 > i + 1 >i+1 >i+1 的位置匹配,由于一个匹配好,另一个多出未匹配的一个,一消一加,刚好抵消掉,所以 j j j 不变,那么:
    • f i , j , k × j → f i + 1 , j , k + 2 j f_{i,j,k}\times j\rightarrow f_{i+1,j,k+2j} fi,j,k×jfi+1,j,k+2j,老鼠向 > i + 1 >i+1 >i+1 的位置匹配,洞会选择一个 ≤ i + 1 \le i+1 i+1 的老鼠,此时有 j j j 只老鼠是还未匹配的,所以任选一个,要乘 j j j
    • f i , j , k × j → f i + 1 , j , k + 2 j f_{i,j,k}\times j\rightarrow f_{i+1,j,k+2j} fi,j,k×jfi+1,j,k+2j,洞向 > i + 1 >i+1 >i+1 的位置匹配,后面分析完全一样
  • i + 1 i+1 i+1 号老鼠和洞都与 ≤ i + 1 \le i+1 i+1 的位置匹配,那么老鼠有 j j j 种选择,洞有 j j j 种选择,乘法原理总共是 j 2 j^2 j2 种,那么 f i , j , k × j 2 → f i + 1 , j − 1 , k + 2 j f_{i,j,k}\times j^2\rightarrow f_{i+1,j-1,k+2j} fi,j,k×j2fi+1,j1,k+2j
  • i + 1 i+1 i+1 号老鼠与 i + 1 i+1 i+1 号洞匹配,那么 j j j 不变: f i , j , k → f i + 1 , j , k + 2 j f_{i,j,k}\rightarrow f_{i+1,j,k+2j} fi,j,kfi+1,j,k+2j

综上所述,转移分如下 3 3 3 种:

  • f i , j , k → f i + 1 , j + 1 , k + 2 j f_{i,j,k}\rightarrow f_{i+1,j+1,k+2j} fi,j,kfi+1,j+1,k+2j
  • f i , j , k × ( 2 j + 1 ) → f i + 1 , j , k + 2 j f_{i,j,k}\times (2j+1)\rightarrow f_{i+1,j,k+2j} fi,j,k×(2j+1)fi+1,j,k+2j
  • f i , j , k × j 2 → f i + 1 , j − 1 , k + 2 j f_{i,j,k}\times j^2\rightarrow f_{i+1,j-1,k+2j} fi,j,k×j2fi+1,j1,k+2j

A c c e p t e d   C o d e \mathrm{Accepted\ Code} Accepted Code
#include <bits/stdc++.h>
#define int long long

using namespace std;

typedef pair<int, int> PII;
typedef long long LL;

const int MAXN = 55, MOD = 1e9 + 7;

int n, m;
int f[MAXN][MAXN][MAXN * MAXN];

void add(int &a, int b) { a = (a + b) % MOD; }

signed main()
{
	cin.tie(0);
	cout.tie(0);
	ios::sync_with_stdio(0);

	cin >> n >> m;

	f[0][0][0] = 1;
	for (int i = 0; i <= n; i ++)
		for (int j = 0; j <= i; j ++)
			for (int k = 0; k <= m; k ++)
			{
				add(f[i + 1][j + 1][k + 2 * j], f[i][j][k]); //洞与老鼠均与 > i 的老鼠与洞匹配
				add(f[i + 1][j][k + 2 * j], f[i][j][k] * (2 * j + 1) % MOD); //洞或老鼠中的一个与 > i 的位置匹配,令一个匹配内部;或该洞与该老鼠匹配
				if (j) add(f[i + 1][j - 1][k + 2 * j], f[i][j][k] * j * j); //均与 < i 且未匹配的点匹配
			}

	cout << f[n][0][m] << endl;

	return 0;
}

C F 1838 E − C o u n t   S u p e r s e q u e n c e s \mathrm{CF1838E - Count\ Supersequences} CF1838ECount Supersequences

D e s c r i p t i o n \mathrm{Description} Description

给定一个长为 n n n 的序列 a a a ∀ i ∈ [ 1 , n ] , a i ∈ [ 1 , k ] \forall i\in[1,n],a_i\in[1,k] i[1,n],ai[1,k],有序列 b b b,满足 a a a b b b 的子序列, b i ∈ [ 1 , k ] b_i \in [1,k] bi[1,k] b b b 的长度为 m m m

求满足条件的 b b b 数组个数。


S o l u t i o n \mathrm{Solution} Solution

首先,考虑如何判断序列 A A A 是序列 B B B 的子序列,那么贪心的考虑必定是先找到 A 1 A_1 A1 B B B 中出现的最早的位置 p 1 p_1 p1 A 2 A_2 A2 B B B 序列出现的最早的位置 p 2 p_2 p2,使得 p 2 > p 1 p_2>p_1 p2>p1 … \dots A n A_n An B B B 序列出现最早的位置 p n p_n pn,使得 p n > p n − 1 p_n>p_{n-1} pn>pn1

那么,这个问题是给定子序列 A A A,求解有多少序列 B B B,使得 A A A B B B 的子序列。一个 N a i v e Naive Naive 的想法是在 A A A 序列每个位置前后插入数(类似隔板法),不过这样会算重。

那么,类似上面求子序列的过程,可以通过类似枚举 p p p 序列的方式来计数:
∑ p 1 < p 2 < ⋯ < p n ( k − 1 ) p n − n k m − p n \sum_{p_1<p_2<\dots<p_n} (k-1)^{p_n-n}k^{m-p_n} p1<p2<<pn(k1)pnnkmpn
一个最暴力的式子, k − 1 k-1 k1 是因为前 p n p_n pn 个数每个可以选择的位置(共 p n − n p_n-n pnn 个),都不能与后面接下来的 p p p 所对应的数重复,如果重复了,那么按照我们贪心的选择,必定会将 p p p 选择到该位置,所以只能填 k − 1 k-1 k1 个数,但是 p n p_n pn 后面的数就无所谓了!

继续狂推式子:
∑ p 1 < p 2 < ⋯ < p n ( k − 1 ) p n − n k m − p n = ∑ p n = n m ( p n − 1 n − 1 ) ( k − 1 ) p n − n k m − p n = ∑ p n = n m ( p n − 1 n − 1 ) ( k − 1 ) p n − n ∑ i = 0 m − p n ( m − p n i ) ( k − 1 ) m − p n − i = ∑ p n = n m ∑ i = 0 m − p n ( p n − 1 n − 1 ) ( m − p n i ) ( k − 1 ) m − p n − i + p n − n = ∑ p n = n m ∑ i = 0 m − p n ( p n − 1 n − 1 ) ( m − p n i ) ( k − 1 ) m − n − i = ( k − 1 ) m ∑ i = 0 m ( k − 1 ) − n − i ∑ p n = n m ( p n − 1 n − 1 ) ( m − p n i ) = ( k − 1 ) m ∑ i = 0 m ( k − 1 ) − n − i ( m n + i ) = ( k − 1 ) m ∑ i = n m ( k − 1 ) − i ( m i ) = ∑ i = n m ( k − 1 ) m − i ( m i ) \begin{align*} &\sum_{p_1<p_2<\dots<p_n} (k-1)^{p_n-n}k^{m-p_n}\\ =&\sum_{p_n=n}^m\binom{p_n-1}{n-1}(k-1)^{p_n-n}k^{m-p_n}\\ =&\sum_{p_n=n}^m\binom{p_n-1}{n-1}(k-1)^{p_n-n}\sum_{i=0}^{m-p_n}\binom{m-p_n}{i}(k-1)^{m-p_n-i}\\ =&\sum_{p_n=n}^m\sum_{i=0}^{m-p_n}\binom{p_n-1}{n-1}\binom{m-p_n}{i}(k-1)^{m-p_n-i+p_n-n}\\ =&\sum_{p_n=n}^m\sum_{i=0}^{m-p_n}\binom{p_n-1}{n-1}\binom{m-p_n}{i}(k-1)^{m-n-i}\\ =&(k-1)^m\sum_{i=0}^{m}(k-1)^{-n-i}\sum_{p_n=n}^m\binom{p_n-1}{n-1}\binom{m-p_n}{i}\\ =&(k-1)^m\sum_{i=0}^{m}(k-1)^{-n-i}\binom{m}{n+i}\\ =&(k-1)^m\sum_{i=n}^{m}(k-1)^{-i}\binom{m}{i}\\ =&\sum_{i=n}^{m}(k-1)^{m-i}\binom{m}{i}\\ \end{align*} ========p1<p2<<pn(k1)pnnkmpnpn=nm(n1pn1)(k1)pnnkmpnpn=nm(n1pn1)(k1)pnni=0mpn(impn)(k1)mpnipn=nmi=0mpn(n1pn1)(impn)(k1)mpni+pnnpn=nmi=0mpn(n1pn1)(impn)(k1)mni(k1)mi=0m(k1)nipn=nm(n1pn1)(impn)(k1)mi=0m(k1)ni(n+im)(k1)mi=nm(k1)i(im)i=nm(k1)mi(im)
好,在一番辛苦的推倒下也是成功的推到了一个简单的式子,不过还是得 O ( m ) O(m) O(m) 做,还是过不去。我们先来讲解一下式子的含义吧:

  • 第一步,由于后面只跟 p n p_n pn 有关,所以只用枚举 p n p_n pn,剩余的 p 1 ∼ n − 1 p_{1\sim n-1} p1n1,可以用组合数算,即在 p n − 1 p_n-1 pn1 个位置里选 n − 1 n-1 n1 个数
  • 第二步,通过 二项式定理 k m − p n k^{m-p_n} kmpn 化为 ( ( k − 1 ) + 1 ) m − p n ((k-1)+1)^{m-p_n} ((k1)+1)mpn
  • 第三步,将第二个 ∑ \sum 提到前面,并将两个 ( k − 1 ) (k-1) (k1) 的指数次幂合并
  • 第四步,化简 ( k − 1 ) (k-1) (k1) 的指数次幂
  • 第五步,将 ( k − 1 ) m − n − i (k-1)^{m-n-i} (k1)mni 替换为 ( k − 1 ) m ( k − 1 ) − n − i (k-1)^m(k-1)^{-n-i} (k1)m(k1)ni,并将 ( k − 1 ) m (k-1)^m (k1)m 提前, ( k − 1 ) − n − i (k-1)^{-n-i} (k1)ni 放置在第一个 ∑ \sum 的后面
  • 第六步,观察发现后面的两个组合数相乘,其实可以用上文的组合数卷积,因为上指标的和总是一个定值。
  • 第七步,将 i i i 替换为 i + n i+n i+n
  • 第八步,将 ( k − 1 ) m (k-1)^m (k1)m 再合并回 ( k − 1 ) − i (k-1)^{-i} (k1)i 得到 ( k − 1 ) m − i (k-1)^{m-i} (k1)mi

那么,如何继续优化呢?观察可知,如果 ∑ \sum i i i 0 0 0 开始枚举,其实就是二项式定理,那么不妨令其从 0 0 0 开始,后面在减去即可:
∑ i = n m ( k − 1 ) m − i ( m i ) = ∑ i = 0 m ( k − 1 ) m − i ( m i ) − ∑ i = 0 n − 1 ( k − 1 ) m − i ( m i ) = k m − ∑ i = 0 n − 1 ( k − 1 ) m − i ( m i ) \begin{align*} &\sum_{i=n}^{m}(k-1)^{m-i}\binom{m}{i}\\ =&\sum_{i=0}^m(k-1)^{m-i}\binom{m}{i}-\sum_{i=0}^{n-1}(k-1)^{m-i}\binom{m}{i}\\ =&k^m-\sum_{i=0}^{n-1}(k-1)^{m-i}\binom{m}{i} \end{align*} ==i=nm(k1)mi(im)i=0m(k1)mi(im)i=0n1(k1)mi(im)kmi=0n1(k1)mi(im)
这样,我们就可以在 O ( n log ⁡ m ) O(n\log m) O(nlogm) 的时间复杂度内解决该问题了!

A c c e p t e d   C o d e \mathrm{Accepted\ Code} Accepted Code
#include <bits/stdc++.h>
#define int long long

using namespace std;

typedef pair<int, int> PII;
typedef long long LL;

const int MOD = 1e9 + 7;

int ksm(int a, int b, int p)
{
	int Result = 1;
	while (b)
	{
		if (b & 1) Result = Result * a % p;
		a = a * a % p;
		b >>= 1;
	}

	return Result;
}

void solve()
{
	int n, m, k, a;

	cin >> n >> m >> k;

	for (int i = 1; i <= n; i ++)
		cin >> a;

	int res = (ksm(k, m, MOD) - ksm(k - 1, m, MOD) + MOD) % MOD, up = 1, dw = 1;
	for (int i = 1; i < n; i ++)
	{
		up = up * (m - i + 1) % MOD, dw = dw * ksm(i, MOD - 2, MOD) % MOD;
		res = (res - (up * dw % MOD * ksm(k - 1, m - i, MOD) % MOD) + MOD) % MOD;
	}

	cout << res << endl;
}

signed main()
{
	cin.tie(0);
	cout.tie(0);
	ios::sync_with_stdio(0);

	int Data;

	cin >> Data;

	while (Data --)
		solve();

	return 0;
}

组合谔谔天地灭,代数推导保平安!
  • 11
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值