[UVALive - 7040]Color [容斥 恰好k个]

本文探讨了使用m种颜色对线性格子进行染色,且相邻格子颜色不同的情况下,求恰好使用k种颜色的染色方案数的算法。通过容斥原理,详细解析了如何计算至多使用k种颜色的方案数,并进一步求得恰好k种颜色的方案数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在这里插入图片描述

题意: 用m种颜色染一个线性格子,相邻格子颜色不同,问恰好染k种颜色的方案数.
至多用k个颜色的方案数目为 k∗(k−1)k * (k - 1)k(k1)
但是要求的是恰好k个颜色,考虑容斥
AiA_iAi 表示不用i号颜色i号颜色i 然后考虑对立面,
∣A1‾∩A2‾∩...∩Ak‾∣| \overline{A_1} \cap \overline{A_2} \cap...\cap\overline{A_k} | A1A2...Ak =k∗(k−1)n−1−∣A1∪A2∪...∪Ak∣=k * (k - 1)^{n-1} -|{A_1}\cup{A_2}\cup{...}\cup{A_k}|=k(k1)n1A1A2...Ak
=k∗(k−1)n−1−∑∣Ai∣+∑∣Ai∩Aj∣+...+(−1)k∑∣A1∩A2∩...∩Ak∣= k*(k-1)^{n-1}-\sum|A_i|+\sum|A_i\cap A_j|+...+(-1)^k\sum|A_1 \cap A_2\cap...\cap A_k|=k(k1)n1Ai+AiAj+...+(1)kA1A2...Ak
∑∣Ai∣=Ck1∗(k−1)∗(k−2)n−1\sum|A_i| = C_k^1*(k-1)*(k-2)^{n-1}Ai=Ck1(k1)(k2)n1
∑∣Ai1∩Ai2∩...∩Aij∣=Ckj∗(k−j)∗(k−j−1)n−1\sum|A_{i_1} \cap A_{i_2}\cap...\cap A_{i_j}| = C_k^{j}*(k-j)*(k-j-1)^{n-1}Ai1Ai2...Aij=Ckj(kj)(kj1)n1
ans=∣A1‾∩A2‾∩...∩Ak‾∣∗Cmkans=| \overline{A_1} \cap \overline{A_2} \cap...\cap\overline{A_k} |*C_m^kans=A1A2...AkCmk
CmkC_{m}^{k}Cmk 可以利用递推得到:Cmk=Cmk−1∗m−k+1kC_{m}^{k} = C_{m}^{k-1} * \frac {m-k+1}{k}Cmk=Cmk1kmk+1
代码

/***********************************************
Author        :lzs
Created Time  :2018年10月19日 星期五 17时49分21秒
File Name     :Color.cpp
 ************************************************/
#include <bits/stdc++.h>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <string>
#include <math.h>
#include <stack>
#include <vector>
#include <queue>
#include <set>
#include <map>
using namespace std;
#define rep(i, l, r) for(int i = l; i < r; i++)
#define per(i, r, l) for(int i = r; i >= l; i--)
#define dbgln(...) cerr<<"["<<#__VA_ARGS__":"<<(__VA_ARGS__)<<"]"<<"\n"
#define dbg(...) cerr<<"["<<#__VA_ARGS__":"<<(__VA_ARGS__)<<"]"


typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int>pii;

const int N = (int) 1e6 + 20;
const int M = (int) 1e6 + 11;
const int MOD = (int) 1e9 + 7;
const int INF = (int) 0x3f3f3f3f;
const ll INFF = (ll) 0x3f3f3f3f3f3f3f3f;
/*-----------------------------------------------------------*/

int Pow(int a, int b, int c = MOD){
	int s = 1; a %= c;
	while(b){
		if(b & 1) s = s * 1ll * a % c;
		b >>= 1;
		a = a * 1ll * a % c;
	}
	return s;
}
int fac[N], inv[N], in[N];
void ini(int n){
	fac[0] = 1;
	for(int i = 1; i < n; i++) fac[i] = i * 1ll * fac[i - 1] % MOD, in[i] = Pow(i, MOD - 2);
	inv[n - 1] = Pow(fac[n - 1], MOD - 2); inv[0] = 1;
	for(int i = n - 2; i > 0; i--) inv[i] = inv[i + 1] * 1ll * (i + 1) % MOD;
}
int C(int n, int m){
	if(n < m) return 0;
	return fac[n] * 1ll * inv[n - m] % MOD * inv[m] % MOD;
}
int main(){
	ini(1000000 + 10);
	int T; scanf("%d" ,&T);
	for(int cas = 1; cas <= T; cas++){
		int n, m, k; scanf("%d%d%d", &n, &m, &k);
		int cmk = 1;
		for(int i = 1; i <= k; i++) 
			cmk = cmk * 1ll * (m - i + 1) % MOD * in[i] % MOD;
		ll ans = k * 1ll * Pow(k - 1, n - 1) % MOD;
		int cur = -1;
		for(int i = 1; i < k; i++){
			ans += cur * 1ll * C(k, i) * (k - i) % MOD * Pow(k - i - 1, n - 1) % MOD;  
			ans = (ans + MOD) % MOD;
			cur = -cur;	
		}
		ans = ans * 1ll * cmk % MOD;
		printf("Case #%d: %lld\n", cas, ans);
	}	
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值