[日常训练] 传统题


Solution

  • 一道组合计数好题,orz zzq。
  • 对原问题进行转化,答案 = ∑ i = 1 n f ( a n s = i ) × i = ∑ i = 1 n f ( a n s ≥ i ) = ∑ i = 1 n ( m n − f ( a n s &lt; i ) ) = n × m n − ∑ i = 0 n − 1 f ( a n s ≤ i ) ( f 为 方 案 数 ) = \sum \limits_{i = 1}^{n} f(ans = i) \times i = \sum \limits_{i = 1}^{n} f(ans \ge i) = \sum \limits_{i = 1}^{n}(m ^ n - f(ans &lt; i)) = n \times m^n - \sum \limits_{i = 0}^{n - 1}f(ans \le i)(f为方案数) =i=1nf(ans=i)×i=i=1nf(ansi)=i=1n(mnf(ans<i))=n×mni=0n1f(ansi)(f)
  • 考虑如何计算 ∑ i = 0 n − 1 f ( a n s ≤ i ) \sum \limits_{i = 0}^{n - 1}f(ans \le i) i=0n1f(ansi)
  • 先把序列分成 j j j 个块,相邻的块颜色不同,染色的方案数为 m ( m − 1 ) j − 1 m(m - 1)^{j - 1} m(m1)j1
  • 计算分成 j j j 个块的方案要考虑 a n s ≤ i ans \le i ansi 的限制,直接计算并不容易,我们通过容斥,枚举 k k k 个长度 &gt; i &gt; i >i 的块,把它们的长度减去 i i i,剩下的部分就可以直接用隔板法计算。
  • 因此 ∑ i = 0 n − 1 f ( a n s ≤ i ) = ∑ i = 0 n − 1 ∑ j = 1 n m ( m − 1 ) j − 1 ∑ k = 0 j ( − 1 ) k C j k C n − i k − 1 j − 1 \sum \limits_{i = 0}^{n - 1} f(ans \le i) = \sum \limits_{i = 0}^{n - 1} \sum \limits_{j = 1}^{n}m(m - 1)^{j - 1} \sum \limits_{k = 0}^{j} (-1)^kC_{j}^{k}C_{n - ik - 1}^{j - 1} i=0n1f(ansi)=i=0n1j=1nm(m1)j1k=0j(1)kCjkCnik1j1
  • 考虑简化这个式子,把无关项外移,先枚举 k k k 再枚举 j j j,得到 = m ∑ i = 0 n − 1 ∑ k = 0 n ( − 1 ) k ∑ j = max ⁡ { k , 1 } n ( m − 1 ) j − 1 C j k C n − i k − 1 j − 1 =m\sum \limits_{i = 0}^{n - 1}\sum\limits_{k = 0}^{n}(-1)^k\sum \limits_{j = \max\{k, 1\}}^{n}(m - 1)^{j - 1}C_{j}^{k}C_{n - ik - 1}^{j - 1} =mi=0n1k=0n(1)kj=max{k,1}n(m1)j1CjkCnik1j1
  • 考虑组合数的限制 k − 1 ≤ j − 1 ≤ n − i k − 1 k - 1 \le j - 1 \le n - ik - 1 k1j1nik1,所以 k ≤ n − i k , ( i + 1 ) k ≤ n k \le n - ik, (i + 1)k \le n knik,(i+1)kn,枚举 i , k i, k i,k 的复杂度为调和级数。
  • 现在只要能够快速计算枚举 j j j 的部分,问题就能解决了。
  • 先做一些简单的变换: C n − i k − 1 j − 1 = ( n − i k − 1 ) ! ( j − 1 ) ! ( n − i k − j ) ! = j n − i k × ( n − i k ) ! j ! × ( n − i k − j ) ! = j n − i k C n − i k j C_{n - ik - 1}^{j - 1} = \frac{(n - ik - 1)!}{(j - 1)!(n - ik - j)!} = \frac{j}{n - ik} \times \frac{(n - ik)!}{j! \times (n - ik - j)!} = \frac{j}{n - ik}C_{n - ik}^{j} Cnik1j1=(j1)!(nikj)!(nik1)!=nikj×j!×(nikj)!(nik)!=nikjCnikj
  • 代入原式,得到 = m ∑ i = 0 n − 1 ∑ k = 0 n ( − 1 ) k n − i k ∑ j = max ⁡ { k , 1 } n j ( m − 1 ) j − 1 C j k C n − i k j =m\sum \limits_{i = 0}^{n - 1}\sum\limits_{k = 0}^{n}\frac{(-1)^k}{n - ik}\sum \limits_{j = \max\{k, 1\}}^{n}j(m - 1)^{j - 1}C_{j}^{k}C_{n - ik}^{j} =mi=0n1k=0nnik(1)kj=max{k,1}nj(m1)j1CjkCnikj
  • 考虑寻找枚举 j j j 部分的组合意义:
  1. n − i k n - ik nik 个点中选 j j j 个点( C n − i k j C_{n - ik}^{j} Cnikj);
  2. j j j 个点中选 k k k 个点( C j k C_{j}^{k} Cjk);
  3. j j j 个点中选一个关键点( j j j);
  4. j j j 个点中除关键点外的点用 m − 1 m - 1 m1 种颜色染色( ( m − 1 ) j − 1 (m - 1)^{j - 1} (m1)j1)。
  • 注意到若该组合意义下存在方案,要满足 j j j 至少为 1。
  • 考虑先枚举选 k k k 个点:
  1. n − i k n - ik nik 个点中选 k k k 个点 ( C n − i k k C_{n - ik}^{k} Cnikk);
  2. 分两种情况讨论:
    1. 关键点不在 k k k 个点之中:
      [1] 将 k k k 个点用 m − 1 m - 1 m1 种颜色染色( ( m − 1 ) k (m - 1)^{k} (m1)k);
      [2] 在 n − i k − k n - ik - k nikk 个点中选一个关键点( n − i k − k n - ik - k nikk);
      [3] 在剩下的 n − i k − k − 1 n - ik - k - 1 nikk1 点中选一个子集用 m − 1 m - 1 m1 种颜色染色,相当于将这 n − i k − 1 n - ik - 1 nik1 个点用 m m m 种颜色染色( m n − i k − k − 1 m^{n - ik - k - 1} mnikk1);
    2. 关键点在 k k k 个点之中:
      [1] 在 k k k 个点中选一个关键点( k k k);
      [2] 将其余 k − 1 k - 1 k1 个点用 ( m − 1 ) (m - 1) (m1) 种颜色染色( ( m − 1 ) k − 1 (m - 1)^{k - 1} (m1)k1);
      [3] 在剩下的 n − i k − k n - ik - k nikk 个点中选一个子集用 m − 1 m - 1 m1 种颜色染色,计算与上面同理( m n − i k − k m^{n - ik - k} mnikk
  • 最终整理得到答案的式子为: n × m n − m ∑ i = 0 n − 1 ∑ k = 0 n ( − 1 ) k n − i k C n − i k k [ ( n − i k − k ) ( m − 1 ) k m n − i k − k − 1 + k ( m − 1 ) k − 1 m n − i k − k ] n \times m^{n} - m\sum \limits_{i = 0}^{n - 1} \sum \limits_{k = 0}^{n} \frac{(-1)^k}{n - ik} C_{n - ik}^{k}[(n - ik - k)(m - 1)^k m^{n - ik - k -1} + k(m - 1)^{k - 1} m^{n - ik - k}] n×mnmi=0n1k=0nnik(1)kCnikk[(nikk)(m1)kmnikk1+k(m1)k1mnikk]
  • 预处理 m , m − 1 m, m - 1 m,m1 的次幂以及 1 1 1~ n n n 阶乘、 1 1 1~ n n n 逆元、 1 1 1~ n n n 阶乘逆元,即可在 O ( n log ⁡ n ) O(n \log n) O(nlogn) 的复杂度内计算上述式子。

Code

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cctype>
#include <cstdio>
#include <cmath>
#include <ctime>

template <class T>
inline void read(T &res)
{
	char ch; bool flag = false; res = 0;
	while (ch = getchar(), !isdigit(ch) && ch != '-');
	ch == '-' ? flag = true : res = ch ^ 48;
	while (ch = getchar(), isdigit(ch))
		res = res * 10 + ch - 48;
	flag ? res = -res : 0; 
}

const int N = 3e5 + 5;
int fra[N], inv_fra[N], inv[N], ex_m[N], ex_m1[N];
int n, m, mod;

inline void add(int &x, int y)
{
	x += y;
	x >= mod ? x -= mod : 0;
}

inline int quick_pow(int x, int k)
{
	int res = 1;
	while (k)
	{
		if (k & 1) res = 1ll * res * x % mod;
		x = 1ll * x * x % mod; k >>= 1;
	}
	return res;
}

inline int C(int n, int m)
{
	return 1ll * fra[n] * inv_fra[n - m] % mod * inv_fra[m] % mod;
}

int main()
{
	freopen("sequence.in", "r", stdin);
	freopen("sequence.out", "w", stdout);
	
	read(n); read(m); read(mod);
	ex_m[0] = ex_m1[0] = fra[0] = 1;
	for (int i = 1; i <= n; ++i)
	{
		fra[i] = 1ll * fra[i - 1] * i % mod;
		ex_m[i] = 1ll * ex_m[i - 1] * m % mod;
		ex_m1[i] = 1ll * ex_m1[i - 1] * (m - 1) % mod;
	}
	
	inv_fra[n] = quick_pow(fra[n], mod - 2);
	for (int i = n; i >= 1; --i)
		inv_fra[i - 1] = 1ll * inv_fra[i] * i % mod;
	inv[0] = 1;
	for (int i = 1; i <= n; ++i)
		inv[i] = 1ll * fra[i - 1] * inv_fra[i] % mod;
	
	int ans = 0;
	for (int i = 0; i < n; ++i)
		for (int k = 0, km = n / (i + 1); k <= km; ++k)
		{
			int t = n - i * k - k,                                                       
				sit1 = 1ll * k * (k > 0 ? ex_m1[k - 1] : 0) % mod * ex_m[t] % mod,
				sit2 = 1ll * t * ex_m1[k] % mod * (t > 0 ? ex_m[t - 1] : 0) % mod;
			int tmp = 1ll * inv[t + k] * C(t + k, k) % mod * (sit1 + sit2) % mod;
			add(ans, (k & 1) ? mod - tmp : tmp);
		}
	ans = 1ll * (mod - m) * ans % mod;
	add(ans, 1ll * n * ex_m[n] % mod);
	printf("%d\n", ans);
	
	fclose(stdin); fclose(stdout);
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值