当前位置: 首页 > news >正文

2025.6.10【ZR NOI模拟赛 T3】 过啥题 题解(Lucas 定理, 数位dp, 组合意义)

为什么70分这么唐考试没写啊。。。。

题意

T T T 组数据, 每组给定 n , m , k n, m, k n,m,k, 对于所有长度为 k k k 并且满足以下条件的 正整数 数列 { a i } \{a_i\} {ai} { b i } \{b_i\} {bi}

  • ∑ i = 1 k a i = n \sum\limits_{i = 1}^{k}a_i = n i=1kai=n
  • ∑ i = 1 k b i = m \sum\limits_{i = 1}^{k}b_i = m i=1kbi=m

计算 ∏ i = 1 k min ⁡ ( a i , b i ) \prod\limits_{i = 1}^{k}\min(a_i,b_i) i=1kmin(ai,bi) 的和。对 10007 10007 10007 取模。

1 ≤ T ≤ 100 , 1 ≤ k ≤ n , m ≤ 10 18 1 \leq T \leq 100, 1 \leq k \leq n,m \leq 10^{18} 1T100,1kn,m1018

分析

注意到模数比较小, 并且 n , m n, m n,m 范围比较大,感觉起来就是数位 d p dp dp 题。

先来尝试给 ∏ i = 1 k min ⁡ ( a i , b i ) \prod\limits_{i = 1}^{k}\min(a_i,b_i) i=1kmin(ai,bi) 编一个组合意义:发现它就等于 有多少长度为 k k k 的正整数序列 { x i } \{x_i\} {xi},满足 ∀ i ∈ [ 1 , k ] , x i ≤ a i 且 x i ≤ b i \forall i \in[1, k], x_i \leq a_i 且 x_i \leq b_i i[1,k],xiaixibi

那么可以枚举 { x i } \{x_i\} {xi},计算一个 { x i } \{x_i\} {xi} 会被算几次。在 x i x_i xi 的基础上调整 a i , b i a_i, b_i ai,bi,设枚举的 x i x_i xi 的和为 S S S,那么答案就等于:

∑ S = k min ⁡ ( n , m ) ( S − 1 k − 1 ) ( n − S + k − 1 k − 1 ) ( m − S + k − 1 k − 1 ) \sum\limits_{S = k}^{\min(n, m)}\binom{S - 1}{k - 1}\binom{n -S+k - 1}{k - 1}\binom{m - S+k - 1}{k - 1} S=kmin(n,m)(k1S1)(k1nS+k1)(k1mS+k1)

将式子化的好看一些。令 k ′ = k − 1 , n ′ = n + k ′ − 1 , m ′ = m + k ′ − 1 k' = k - 1,n' = n + k'-1, m' = m + k' - 1 k=k1,n=n+k1,m=m+k1
那么答案为:

∑ S = k ′ min ⁡ ( n ′ , m ′ ) − k ′ ( S k ′ ) ( n ′ − S k ′ ) ( m ′ − S k ′ ) \sum\limits_{S = k'}^{\min(n', m') - k'}\binom{S}{k'}\binom{n' -S}{k'}\binom{m' - S}{k'} S=kmin(n,m)k(kS)(knS)(kmS)

注意到 S S S 如果不在 [ k ′ , min ⁡ ( n ′ , m ′ ) − k ′ ] [k', \min(n', m') - k'] [k,min(n,m)k] 之间那么这个式子的值肯定等于 0 0 0,因此可以认为对 S S S 的范围没有限制。

根据 卢卡斯定理,可以将组合数拆成 m o d mod mod 进制下每位求组合数然后相乘。我们不能枚举 S S S,考虑在 m o d mod mod 进制下依次考虑 S S S 的每一位。

此时 k ′ , n ′ , m ′ k', n',m' k,n,m 固定,先把它们在 m o d mod mod 进制下的每一位求出来,用 0 0 0 将最高位补到 max ⁡ ( n ′ , m ′ ) \max(n', m') max(n,m) 的最高位。

那么 n ′ − S n' - S nS 就是每一位分别相减,由于是减法,可能会涉及退位,因此我们从高位到低位 d p dp dp

发现由于 S S S 没有范围限制,那么状态只需要记录是否退位。设 d p x , 0 / 1 , 0 / 1 dp_{x, 0/1, 0/1} dpx,0/1,0/1 表示从高到低考虑到第 x x x 位, n ′ − S n' - S nS 是否向 x − 1 x - 1 x1 位退位, m ′ − S m' - S mS 是否向 x − 1 x - 1 x1 位退位的方案数。转移就是枚举第 x − 1 x - 1 x1 位上 S S S 的数字,然后乘上这一位上组合数的贡献。

最后答案就是 d p 0 , 0 , 0 dp_{0, 0, 0} dp0,0,0

复杂度 O ( T × m o d × log ⁡ m o d max ⁡ ( n , m ) ) O(T \times mod \times \log_{mod}\max(n, m)) O(T×mod×logmodmax(n,m))

CODE:

// 首先需要根据组合意义推导出来一个简洁的式子。 然后发现模数比较小, 并且形式比较好
// 可以根据lucas定理拆成 mod 进制下数位dp
// 复杂度是 T * mod * log_{mod}max(n, m)
#include<bits/stdc++.h>
#define pb emplace_back
using namespace std;
typedef long long LL;
const int N = 1e5 + 10;
const int mod = 10007;
int fac[mod], inv[mod]; 
LL n, m, k;
int dp[6][2][2]; // dp[x][0/1][0/1] 表示从高到低考虑到第 x 位, 前面确定了 i 的若干位, n - i 前面往 x - 1 是否退位, m - i 往 x - 1 是否退位的方案数之和
inline int Pow(int x, int y) {int res = 1, k = x;while(y) {if(y & 1) res = res * k % mod;y >>= 1;k = k * k % mod;}return res;
}
inline int C(int n, int m) {if(n < m) return 0;return fac[n] * inv[m] % mod * inv[n - m] % mod;
}
inline int solve(LL n, LL m, LL k) {vector< int > N, M, K;while(n) N.pb(n % mod), n /= mod;while(m) M.pb(m % mod), m /= mod;while(k) K.pb(k % mod), k /= mod;int sz = max({N.size(), M.size(), K.size()});while(N.size() < sz) N.pb(0);while(M.size() < sz) M.pb(0);while(K.size() < sz) K.pb(0);memset(dp, 0, sizeof dp);dp[sz][0][0] = 1;for(int i = sz; i > 0; i -- ) // 刷表for(int a = 0; a <= 1; a ++ ) for(int b = 0; b <= 1; b ++ ) if(dp[i][a][b]) for(int c = 0; c <= 1; c ++ ) for(int d = 0; d <= 1; d ++ ) for(int x = 0; x < mod; x ++ ) {int u = N[i - 1] - x + a * mod - c, v = M[i - 1] - x + b * mod - d;if(u >= 0 && u < mod && v >= 0 && v <= mod) dp[i - 1][c][d] = (dp[i - 1][c][d] + dp[i][a][b] * C(x, K[i - 1]) % mod * C(u, K[i - 1]) % mod * C(v, K[i - 1]) % mod) % mod;}return dp[0][0][0];						
}
int main() {fac[0] = 1; for(int i = 1; i < mod; i ++ ) fac[i] = fac[i - 1] * i % mod;inv[mod - 1] = Pow(fac[mod - 1], mod - 2); for(int i = mod - 2; i >= 0; i -- ) inv[i] = inv[i + 1] * (i + 1) % mod;int T; scanf("%d", &T);while(T -- ) {scanf("%lld%lld%lld", &n, &m, &k);k --; n = n + k - 1, m = m + k - 1;printf("%d\n", solve(n, m, k));}return 0;
}
http://www.xdnf.cn/news/971983.html

相关文章:

  • Java设计模式基础问答
  • 通过Wrangler CLI在worker中创建数据库和表
  • QFuture的使用方式
  • vue的created和mounted区别
  • 替代爬虫!亚马逊API采集商品详情实时数据开发教程
  • 《Java开发者进击之路:掌握Spring AI与DL4J,实现AI模型API集成》
  • MCU Keil中支持的变量类型和定义方法
  • 美业门店/个案疗愈门店管理系统具备「活动促销」功能有哪些优势?
  • 多面体编译的循环分块
  • iOS和桌面双端抓包实战经验总结:Sniffmaster与常见工具组合解析
  • 算法工程师工作面试常考问题汇总
  • HarmonyOS 应用开发学习记录 - 从Windows开发者视角看鸿蒙开发
  • RabbitMQ的使用--Spring AMQP(更新中)
  • 期末考试复习总结-《从简单的页面开始(上)》
  • CentOS7下的Nginx部署
  • 行业 |5G六年,互联网改变了什么?
  • WHAT - 组件库开发场景 - 完全无样式的 UI 组件库 Headless UI
  • 看板更新不及时该如何规范
  • jQuery带动画特效的圆形导航菜单特效
  • Playwright 与 Selenium:自动化测试的两大主流工具对比
  • iOS超级签申请流程及环境部署
  • 从深圳崛起的“机器之眼”:赴港乐动机器人的万亿赛道赶考路
  • 二叉树进阶:经典算法题详解
  • AD8539ARZ ADI 精密放大器 电子元器件解析
  • 判断素数两种方法【自用】
  • 【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)
  • 工作中开发的sql总结
  • LeetCode 200.岛屿数量
  • 天猫官方认证TP服务商——品融电商代运营全链路解析
  • 无需安装!在线 SQL 数据库工具实战 :经典 SQL 语句案例