Character Encoding HDU - 6397(容斥原理解决方程解个数经典问题详解)

Character Encoding HDU - 6397

In computer science, a character is a letter, a digit, a punctuation mark or some other similar symbol. Since computers can only process numbers, number codes are used to represent characters, which is known as character encoding. A character encoding system establishes a bijection between the elements of an alphabet of a certain size n and integers from 0 to n−1. Some well known character encoding systems include American Standard Code for Information Interchange (ASCII), which has an alphabet size 128, and the extended ASCII, which has an alphabet size 256.

For example, in ASCII encoding system, the word wdy is encoded as [119, 100, 121], while jsw is encoded as [106, 115, 119]. It can be noticed that both 119+100+121=340 and 106+115+119=340, thus the sum of the encoded numbers of the two words are equal. In fact, there are in all 903 such words of length 3 in an encoding system of alphabet size 128 (in this example, ASCII). The problem is as follows: given an encoding system of alphabet size n where each character is encoded as a number between 0 and n−1 inclusive, how many different words of length m are there, such that the sum of the encoded numbers of all characters is equal to k
?

Since the answer may be large, you only need to output it modulo 998244353.

Input
The first line of input is a single integer T (1≤T≤400), the number of test cases.

Each test case includes a line of three integers n,m,k (1≤n,m≤105,0≤k≤105), denoting the size of the alphabet of the encoding system, the length of the word, and the required sum of the encoded numbers of all characters, respectively.

It is guaranteed that the sum of n, the sum of m and the sum of k don’t exceed 5×106
, respectively.
Output
For each test case, display the answer modulo 998244353 in a single line.
Sample Input

4
2 3 3
2 3 4
3 3 3
128 3 340

Sample Output

1
0
7
903

题意:

题目给你三个数n,m,k,代表每个数的范围在0到n-1,可重复的选择m个数使得m个数之和为k

其实题目意思是让求

i = 1 m x i   =   k

分析:

FUCK!当时我把n给当成集合了,想成了从n个不同元素中有重复选m个使得和为k,这样变成了求方程组

x 1 + x 2 + + x n = m

x 1 a 1 + x 2 a 2 + + x n a n = k

我一看这他妈咋做,然后就不会了

以上是错误想法


下面是正确解法:

其实当时看到这道题的时候还挺激动的,因为前一段时间刚看组合数学那本书的时候容斥那一节正好讲到了类似问题(其实就是那个问题我他妈想成上面那样了),但是因为想错了转化思路,就凉了

这道题实际上给定n只不过给定了每个数的范围 x i [ 0 , n 1 ] ,这样只需满足

x 1 + x 2 + + x m = k

即可,这就转化成容斥原理能解决的经典问题了


什么经典问题呢,下面我就把书上那个例子写下来,估计大家看懂了下面的例子,这个题也就会写了

在讲下面例子前需要一些预备知识

1)从k中元素中有重复的选择选择r种元素的组合数为

C k + r 1 r

证明看这里!

2)对于方程

x 1 + x 2 + + x k = r

的非负整数解即 x i 0 的个数其实就是
C k + r 1 r

这个方程和上面k种元素有重复的选择r个元素的组合数有啥关系呢?

设S的k种类型的对象是 { a 1 , a 2 , , a k } 使得

S = { a 1 , a 2 , , a k }

那么从中有重复的选择r个元素组成的r组合应该呈现

{ x 1 a 1 , x 2 a 2 , , x k a k }

且满足
x 1 + x 2 + + x k = r

因而方程的解的个数等于r组合的个数(我当时做题就把思路给固定在上面集合上了,其实直接考虑这个方程就行)

因此我们发现对于这个方程的解的个数对于 x i 0 的情况直接套公式,而如果 x i n i 呢?也好求,只需要变量代换一下设 y i = x i n i ,这样再把 y i 加起来就得到了新的方程,且此时 y i 0 继续套公式

举个例子

x 1 + x 2 + x 3 + x 4 = 10         ( x 1 1 , x 2 1 , x 3 1 , x 4 1 )
求方程解个数
先变量代换设
y 1 = x 1 1 , y 2 = x 2 1 , y 3 = x 3 1 , y 4 = x 4 1

方程变成
y 1 + y 2 + y 3 + y 4 = 6           ( y 1 0 , y 2 0 , y 3 0 , y 4 0 )

这样再次转化回最简单的情况,直接套公式

我们发现这都是仅仅有下界的情况,可以通过变量代换解决,然后套公式

但是对于有上界的情况,就需要用到容斥定理了

下面就是利用容斥定理完美解决这类问题的经典例子。

例子:

满足

1 x 1 5 ,       2 x 2 4 ,       0 x 3 5 ,       3 x 4 9

的方程
x 1 + x 2 + x 3 + x 4 = 18

的整数解的数目是多少?

我们引入一些新变量

y 1 = x 1 1 ,       y 2 = x 2 + 2 ,       y 3 = x 3 ,       y 4 = x 4 3

这样方程变成了

y 1 + y 2 + y 3 + y 4 = 16                       ( 1 )

关于 x i 的不等式当且仅当

0 y 1 4 ,       0 y 2 6 ,       0 y 3 5 ,       0 y 4 6

设S是方程(1)的所有非负整数解即 x i 0 的集合。S的大小为

| S | = C 16 + 4 1 16 = 969

P 1 y 1 5 的性质, P 2 y 2 7 P 3 y 3 6 P 4 y 4 7

A i S P i ( i = 1 , 2 , 3 , 4 ) 的解组成的子集

因此我们实际上想要计算集合 A ¯ 1 A ¯ 2 A ¯ 3 A ¯ 4 的大小

根据容斥原理,集合 A 1 由S中满足 y 1 5 z 1 = y 1 5 , z 2 = y 2 , z 3 = y 3 , z 4 = y 4

我们看到, A 1 的解的个数与

z 1 + z 2 + z 3 + z 4 = 11

的非负整数解的个数相同,因此
| A 1 | = C 14 11 = 364

以类似方式得到

| A 2 | = C 12 9 = 220 , | A 3 | = C 13 10 = 286 , | A 4 | = C 12 9 = 220

集合 A 1 A 2 是S中满足 y 1 5 , y 2 7 的那些解组成的,进行变量代换( u 1 = y 1 5 , u 2 = y 2 7 , u 3 = y 3 , u 4 = y 4

我们看到 A 1 A 2 的解的个数与

u 1 + u 2 + u 3 + u 4 = 4

的非负整数解的个数相同。因此
| A 1 A 2 | = C 7 4 = 35

以类似的方式得到

| A 1 A 3 | = C 8 5 = 56 , | A 1 A 4 | = C 7 4 = 35

| A 2 A 3 | = C 6 3 = 20 , | A 2 A 4 | = C 5 2 = 10

| A 3 A 4 | = C 6 3 = 20

集合 A 1 , A 2 , A 3 , A 4 中任意三个的交集都是空集,应用容斥原理得到

| A ¯ 1 A ¯ 2 A ¯ 3 A ¯ 4 | = 969 ( 364 + 220 + 286 + 220 ) + ( 35 + 56 + 35 + 20 + 10 + 20 ) = 55


相信大家看了上面的例子就会发现,tmd这个题和上面的例题就是一毛一样的,只要能把问题转化成那种形式,就可以直接套了

这道题因为每个数 0 x i n 1

因此大大简化了这道题

x 1 + x 2 + + x m = k

当且仅当

0 x i n 1

的解的个数

总的集合个数 | S | = C k + m 1 m 1 = C k + m 1 k (用哪个都行)

根据上面的步骤我们进行变量代换实际上就是把右边减去某些数

假设p个变量 x i n 此时应该有 C m p 中情况

对于每种情况经过变量代换后,右边数变成了 k n p

即方程变成

y 1 + y 2 + + y m = k n p

对于一种情况该式的解的种类数为

C k + m n p 1 m 1

共有

C m p
种情况是求上面方程解的个数,根据乘法原理方案数为
C m p C k + m n p 1 m 1

最终工具容斥原理答案

a n s = p = 1 p n k ( 1 ) p C m p C k + m n p 1 m 1

逆元之类的东西预处理出来否则超时

code:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 998244353;
const int maxn = 203000;
ll n,m,k;
ll fac[maxn],finv[maxn];
ll q_pow(ll a,ll b){
        ll ans = 1;
        while(b){
                if(b & 1)
                        ans = ans * a % mod;
                a = a * a % mod;
                b >>= 1;
        }
        return ans;
}

void init(){
        fac[0] = 1;
        for(int i = 1; i < maxn; i++){
                fac[i] = fac[i-1] * i % mod;
        }
        finv[maxn-1] = q_pow(fac[maxn-1],mod-2);
        for(int i = maxn-2; i >= 0; i--){
                finv[i] = finv[i+1] * (i + 1) % mod;
        }
}

ll C(ll n,ll m){
        if(n < 0 || m < 0 || n < m) return 0;
        return fac[n] * finv[m] % mod * finv[n-m] % mod;
}

int main(){
        int T;
        scanf("%d",&T);
        init();
        while(T--){
                scanf("%lld%lld%lld",&n,&m,&k);
                ll ans = 0;
                for(int p = 0; p * n <= k; p++){
                        if(p & 1) ans = (ans - C(m,p) * C(m+k-1-p*n,m-1) % mod + mod) % mod;
                        else ans = (ans + C(m,p) * C(m+k-1-p*n,m-1) % mod) % mod;
                }
                printf("%lld\n",ans);
        }
        return 0;
}

猜你喜欢

转载自blog.csdn.net/codeswarrior/article/details/81906367