news 2026/3/1 7:17:17

组合数学➕动态规划 Codeforces Round 1035 (Div. 2) D. Token Removing

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
组合数学➕动态规划 Codeforces Round 1035 (Div. 2) D. Token Removing

被组合数学+动态规划整的不知天地为何物了,这玩意经常遇到就算了,还经常不会,至此我打算开篇新的篇章专门记录组合数学➕动态规划的ac之路......

简洁题意:在给定整数 n[1,5000] 和 m[1e8,1.01e9] 的情况下,m作为最终答案的模数,n是序列的长度,序列总共有 (n+1) ! 种,序列中的每个元素保证小于等于该元素位置的索引大小,比如序列上第 i 个元素的值只能是 [ 0 , i ] (按这个规则的话总共是有(n+1) ! 种不同的合法序列),对于每个序列,求从左到右遍历一遍的情况下,有几种不同的操作方法,具体操作方法指的是:对于当前 i 位上的元素x,在x非0的情况下,必须选择 [ x , i ]中的任何一个在之前没被选过的数(也就是说1 到 n中的元素只能被选一次,但不要求必须选择),如果x==0,则什么都不能选,可以发现遍历一边序列总共有n次操作(每次操作要么选一个没被选过的非0的数;要么不选,可以认为是选0,0选择的次数不限制),对于一个序列两种操作方法不同指的是只要存在任意次操作不一致(也就是在遍历某位元素时选的数不一样)。求对于 (n+1) ! 种序列的不同的操作方法总和。

比如n=3,一个合法的序列是[0 , 2 , 1 ],那么一共有两种操作方法:一:选0,选2,选1. 二:选0,选2,选3。

要是没看懂题意的可以直接看原题:https://codeforces.com/contest/2119/problem/D

组合数学的套路基本是正面求解等于无解,至少我目前的理解是这样的,那么直接考虑从别的方向入手,直接思考如果是每次都选0(也就是一个元素都不选,这里选元素指的是只选择非0元素)的操作对应的序列有哪些,再考虑操作只选一个元素的序列有哪些,再考虑操作选两个元素的序列有哪些,依次类推到选n个元素的序列有多少个,直接把这些个数累加即是答案。选0个元素毋庸置疑只有一种序列那就是【0,0,0,0,0.....】,选一个元素的话可以只选1或者2或者3....或者n,一个合理的思路是直接从大到小选元素,比如一开始我可以考虑如果只操作一次选最大的元素n的话,那么合理序列只有 [0 ,0 ,0 ,....., val ],最后的这个 val[ 1,n ],因为我可以选择 [ val , n ]的元素,所以可选的序列有n种,说白了就是对于现在的要操作的第k大的元素可以在某一位选的方法有(n-k+1)种,并且可选择的位置有( k - j )个,j 指的是先操作过的更大的元素的个数,现在考虑对于要操作第 i+1 大的数,已经操作了 j 个更大的数的情况下,有两种情况,一个是选择操作第 i+1 大的数( dp[ i+1 ][ j+1 ] = dp[ i ][ j ]*( n - i )*( i - j + 1 ) ),一个是选择不操作它( dp[ i+1 ][ j ] = dp[ i ][ j ] ),对于第一个状态转移方程中的 dp[ i ][ j ] 的含义指的是在前 i 个大的数中,操作了 j 个数,那么还剩( i - j )个多余的位置可以让 第 i+1 大的数选择,为什么可以选?因为对于越大的数它的范围( x, y ) 中的 y 是更大的,完全满足大于第 i+1 大的数,注意第一大的数是n,这里理解可能有点难受,后面有( i - j) 个空余的位置,加上这个数自己本身的位置就是( i - j + 1 )个位置了,其中( n - i )的含义指的是,比如我要选x,那么对于 ( val , x ) 中的 val 的取值有 [1 , x ],总共其实就是 ( n - ( i + 1 ) + 1)种选法,至此思路已经全部讲完了,这题的难点在于建立新的组合计数模型来用动态规划的方式计数。下面是本人ac代码:

#import "bits/stdc++.h" using namespace std; using ll = long long; using lll = __int128; using u64 = unsigned long long; using u128 = __uint128_t; using ldb = long double; using db = double; #define pb push_back #define ps push #define lowbit(x) x&-x #define all(a) a.begin(),a.end() #define all_(a,b) a.begin()+b,a.end() #define rep(i, a, b) for(ll i=a;i<=b;i++) #define per(i, a, b) for(ll i=a;i>=b;i--) #define rall(a) a.rbegin(),a.rend() #define dbg() cerr<<"|--------------------------------------------|"<<endl; #define fir first #define sec second const ll mod = 998244353; const int inf=0x3f3f3f3f; const ll INF=0x3f3f3f3f3f3f3f3f; inline u64 _pow(u64 x, u64 i, u64 mod, bool ok) {u64 ans = 1;while (i) {if (i & 1)ans = (ok ? (u128)ans * x % mod : ans * x);i >>= 1;x = (ok ? (u128)x * x % mod : x * x);}return ans;} std::mt19937_64 rng(time(0)); ll _gcd(ll a,ll b){ll az=__builtin_ctz(a),bz=__builtin_ctz(b);ll shift=min(az,bz);a>>=az;ll dif=0;while (b) {b>>=bz;dif=b-a;if (dif<0)dif=-dif;a=b,b=dif;bz=__builtin_ctz(b);}return a<<shift;} ll _x,_y,_px,_py; void exgcd(ll a,ll b){if(!b)_x=1,_y=0;else{exgcd(b,a%b);_px=_x;_py=_y;_x=_py;_y=_px-a/b*_py;}} u64 mul_mod(u64 a, u64 b, u64 mod) {return (u128)a * b % mod;} u64 pow_mod(u64 a, u64 d, u64 mod) {u64 res = 1;while (d) {if (d & 1) res = mul_mod(res, a, mod);a = mul_mod(a, a, mod);d >>= 1;}return res;} bool miller_rabin(u64 n) {if (n < 2) return false;u64 d = n - 1, s = 0;while ((d & 1) == 0) d >>= 1, ++s;auto check = [&](u64 a) {if (a % n == 0) return true;u64 x = pow_mod(a, d, n);if (x == 1 || x == n - 1) return true;for (u64 i = 1; i < s; ++i) {x = mul_mod(x, x, n);if (x == n - 1) return true;}return false;};u64 bases[] = {2, 325, 9375, 28178, 450775, 9780504, 1795265022, 0};for (u64 a : bases) {if (a == 0) break;if (!check(a)) return false;}return true;} u64 f_rho(u64 x, u64 c, u64 mod) {return (mul_mod(x, x, mod) + c) % mod;} u64 pollard_rho(u64 n) {if (n % 2ULL == 0) return 2;static std::mt19937_64 rng((uint64_t)chrono::steady_clock::now().time_since_epoch().count());while (true) {u64 c = rng() % (n - 1) + 1;u64 x = rng() % n, y = x, d = 1;while (d == 1) {x = f_rho(x, c, n);y = f_rho(f_rho(y, c, n), c, n);u64 diff = x > y ? x - y : y - x;d = _gcd(diff, n);}if (d != n) return d; }} // 上述火车头完全不用管,只需要看下面代码就完全够用 void solve() { ll n,m;cin>>n>>m; vector<vector<ll>>dp(n+1,vector<ll>(n+1,0)); dp[0][0]=1; rep(i,0,n-1) { rep(j,0,i) { (dp[i+1][j+1]+=dp[i][j]*(n-i)%m*(i-j+1)%m)%=m; (dp[i+1][j]+=dp[i][j])%m; } } ll ans=0; rep(i,0,n)(ans+=dp[n][i])%=m; cout<<ans<<endl; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr); ll t;cin>>t; for (ll i=1;i<=t;i++) { // cerr<<"case -> "<<i<<endl; solve(); } return 0; }
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/1 3:10:45

豆包手机为什么会被其他厂商抵制?它的工作原理是什么?

之所以会想写这个&#xff0c;首先是因为在知乎收到了这个推荐的问题&#xff0c;实际上不管是 AutoGLM 还是豆包 AI 手机&#xff0c;会在这个阶段被第三方厂商抵制并不奇怪&#xff0c;比如微信和淘宝一直以来都很抵制这种外部自动化操作&#xff0c;而非这次中兴的 AI 豆包手…

作者头像 李华
网站建设 2026/2/26 17:22:59

Fritzing:从电路新手到专业设计师的3大核心功能解析

Fritzing&#xff1a;从电路新手到专业设计师的3大核心功能解析 【免费下载链接】fritzing-app Fritzing desktop application 项目地址: https://gitcode.com/gh_mirrors/fr/fritzing-app 还在为复杂的电路图而头疼吗&#xff1f;是否希望有一款工具能够像搭积木一样轻…

作者头像 李华
网站建设 2026/2/25 15:27:06

springboot悦茶奶茶店点餐系统-计算机毕业设计源码59419

目录 摘 要 Abstract 第一章 绪 论 1.1 研究背景及意义 1.2 国内外研究现状 1.3 论文组织结构 第二章 关键技术 2.1 Java语言 2.2 B/S框架 2.3 SpringBoot框架 2.4 Vue技术 2.5 MySQL数据库 2.6 微信开发者工具 2.7 小程序框架以及目录结构介绍 第三章 系统分析…

作者头像 李华
网站建设 2026/2/27 13:40:48

3步解锁123云盘VIP特权:告别限速与广告困扰

你是否曾经因为123云盘的下载速度限制而焦急等待&#xff1f;是否被页面中无处不在的广告干扰了使用体验&#xff1f;现在&#xff0c;一个简单易用的浏览器脚本就能帮你彻底解决这些问题。通过本文介绍的123云盘解锁脚本&#xff0c;你无需支付任何费用就能享受到完整的会员级…

作者头像 李华
网站建设 2026/2/27 0:04:45

基于SpringBoot+vue的宠物领养系统

1. 演示地址 后台&#xff1a;http://chongwulingyangxitong.xiaobias.com/chongwulingyangxitong/admin/dist/index.html 前台&#xff1a;http://chongwulingyangxitong.xiaobias.com/chongwulingyangxitong/front/index.html 管理员&#xff1a;admin/admin 用户&#xff1a…

作者头像 李华
网站建设 2026/2/27 23:59:56

Monitorian:多显示器亮度调节的终极解决方案

Monitorian&#xff1a;多显示器亮度调节的终极解决方案 【免费下载链接】Monitorian A Windows desktop tool to adjust the brightness of multiple monitors with ease 项目地址: https://gitcode.com/gh_mirrors/mo/Monitorian 你是否曾经面对多个显示器时&#xff…

作者头像 李华