@description@

定义矩阵 \(A_i\) 是一个大小为 \(p^i*p^i\) 的矩阵,其中 \(p\) 是第 \(c\) 个素数(c 给定),且 \(A_i[x][y] = [C(x, y) \mod p > 0]\)(其中 C(x, y) 是组合数)。

行列从 0 开始计数。

再定义 \(F[i][j]\) 表示 \((A_i)^j\) 中所有元素之和。

求 \(\sum_{i=1}^n\sum_{j=1}^{k}F[i][j]\)。对 10^9 + 7 取模。

Input

第一行包含一个整数 T,然后接下来是 T 组数据:

每一组数据包含三个整数 c, n, k (0 < n ≤ 10^9, 0 < c, k ≤ 10^5)。意义如上。

Output

对于每组数据,输出一个整数表示答案。

Sample Input

1

1 1 1

Sample Output

3

@solution@

看上去这个题非常不可做,先定义了一个矩阵,然后又要求矩阵幂,然后又要把这个矩阵幂中所有元素求和,然后又要把这些矩阵求和的结果再求和。

但是你只需要找到突破口,剩下的部分就一气呵成(其实一气呵成这个词不能这么用。。。今年中考考了这玩意儿,然后做错了,所以印象深刻。。。)

怎么找突破口?你只需要把题目倒过来读注意到矩阵的定义涉及到组合数对素数取模,于是就可以牵扯出 lucas 定理。

lucas 定理是什么?其实很简单。对于 \(C(n, m) \mod p\),我们将 n, m 拆成 p 进制数的形式,即 \(n = n_0 + n_1*p^1 + ..., m = m_0 + m_1*p^1 + ...\)。

于是 lucas 定理告诉我们:\(C(n, m) = C(n_0, m_0)*C(n_1, m_1)*... \mod p\)

证明这个定理也不难,只是与这道题无关所以暂且不提。

很显然 \(C(n, m) \mod p \ge 0\),所以我们只需要判断 \(C(n, m) \mod p = 0\) 是否成立即可。

又因 \(n_i < p, m_i < p\) (因为是 p 进制嘛),所以 \(C(n_i, m_i)\) 不可能含因子 p,故我们只需要对于每一个 i 判断是否 \(C(n_i, m_i) = 0\),而前面那个等价于 \(n_i > m_i\)。

所以 \(A_i[x][y]\) 为 1 等价于在 p 进制下 x 的每一位都 ≤ y 的对应位。

现在考虑 \((A_i)^j[x][y]\) 怎么求。

先试着考虑 \((A_i)^2[x][y]\),可以发现 \((A_i)^2[x][y] = \sum_{z}A_i[x][z]*A_i[z][y]\),只有当 \(A_i[x][z], A_i[z][y]\) 都为 1 时才会产生贡献。

这有点儿像偏序的关系,因为有些传递性和偏序形成链的感觉在里面。

或者用图论的语言,如果 \(A_i[x][y] = 1\) 则 x 向 y 连边。则 \((A_i)^2[x][y]\) 则有点儿像走两步(中途可以停留在原地)从 x 到达 y 的方案数。

从而简单推广,可以得到 \((A_i)^j[x][y]\) 表示走 j 步从 x 到达 y 的方案数。

那么 F[i][j] 的含义是什么?为了计数的方便我们暂且不用图论的语言描述。

F[i][j] 表示长度为 i 的 p 进制的数字串,选出 j+1 个记为 s0, s1, ... sj,对于第 x 位(1≤x≤n)始终满足 s0[x] ≤ s1[x] ≤ ... ≤ sj[x] 的方案总数。

怎么求 F[i][j] 呢?其实也比较简单。因为每一位都是独立的,所以考虑某一位然后乘法原理乘起来即可。

我们发现如果确定了 s0[x], s1[x], ..., sj[x] 分别是哪些数,它们的顺序始终是一定的(即排序过后的顺序)。所以我们相当于是求 x1 + ... + xp = j + 1 的非负整数解的个数。经典的组合数学问题,答案为 C(j+p, j+1)。

于是 F[i][j] = C(j+p, j+1)^i。

于是 \(\sum_{i=1}^n\sum_{j=1}^{k}F[i][j] = \sum_{i=1}^n\sum_{j=1}^{k}C(j+p, j+1)^i = \sum_{j=1}^{k}\sum_{i=1}^nC(j+p, j+1)^i\)。

枚举 j 然后等比数列求和即可。

注意 C(j+p, j+1) 与 C(j+p+1, j+1+1) 之间实际上是有倍数的关系(你可以把它们拆成阶乘形式以观察到这一点)。于是我们可以直接递推而不用预处理阶乘。

@accepted code@

#include<cstdio>
const int MAXM = 1299709;
const int MOD = int(1E9) + 7;
int pow_mod(int b, int p) {
int ret = 1;
while( p ) {
if( p & 1 ) ret = 1LL*ret*b%MOD;
b = 1LL*b*b%MOD;
p >>= 1;
}
return ret;
}
int prm[MAXM + 5], pcnt;
bool nprm[MAXM + 5];
void init() {
for(int i=2;i<=MAXM;i++) {
if( !nprm[i] )
prm[++pcnt] = i;
for(int j=1;1LL*i*prm[j]<=MAXM;j++) {
nprm[i*prm[j]] = true;
if( i % prm[j] == 0 )
break;
}
}
}
int solve(int p, int n, int k) {
int ans = 0, tmp = p;
for(int j=1;j<=k;j++) {
tmp = 1LL*tmp*(j + p)%MOD*pow_mod(j + 1, MOD - 2)%MOD;
if( tmp == 1 )
ans = (ans + n)%MOD;
else ans = (ans + 1LL*(pow_mod(tmp, n + 1) - 1)*pow_mod(tmp - 1, MOD-2)%MOD - 1)%MOD;
}
return (ans + MOD)%MOD;
}
int main() {
init();
int T; scanf("%d", &T);
for(int i=1;i<=T;i++) {
int c, n, k; scanf("%d%d%d", &c, &n, &k);
printf("%d\n", solve(prm[c], n, k));
}
}

@details@

似乎总喜欢废话很多。。。明明一个不是很复杂的题目却写了这么多东西。。。

这道题有一个点就是:等比数列要特判公比为 1 的情况。

。。。虽然数学上经常考这个东西,不过还是没记住。。。

最新文章

  1. 实用SQL语句大全
  2. C语言Notebook
  3. AlertDialog之常见对话框(单选对话框、多选对话框、进度条对话框)
  4. js传递参数中包含+号时的处理方法
  5. OpenCV基本架构[OpenCV 笔记0]
  6. 用JavaScript实现网页动态水印
  7. 如何安装一个优秀的BUG管理平台(转)
  8. Microservice架构模
  9. [ACM] hdu 1671 Phone List (特里)
  10. diff命令参数
  11. github学习(二)
  12. MySQL Flush导致的等待问题
  13. noip2016普及组 题解
  14. DIN(Deep Interest Network of CTR) [Paper笔记]
  15. python学习--Linux下dlib安装(主要是cmake和boost的安装)
  16. hihocoder1391 Country
  17. MongoDb进阶实践之一 如何在Linux(CentOS 7)上安装MongoDB
  18. vscode 缩进改为2空格
  19. VSTO学习问题(一)
  20. Java access to the Domino Objects, Part 1

热门文章

  1. python基础--类的基础使用
  2. 【react】react-bookManager
  3. POJ1485 Sumdiv
  4. php封装的smarty类实例是怎样
  5. 调试R代码中出现的常用的函数
  6. C/C++中运算符优先级汇总
  7. laravel之文件上传
  8. java通过实体类组装报文
  9. 2019.10.24TCP协程处理
  10. vue-cnodejs