2019-2020 10th BSUIR Open Programming Championship. Semifinal 题解
数学签到题比较多。gym103637
Tag
A(概率期望,计数DP)
B(字符串索引)
C(分类讨论)
F(概率期望,NTT卷积)
I(找规律,数论)
J(递推数列,矩阵快速幂)
K(暴力,贪心)
L(模拟,数论)
A. Agile permutation
称第一种操作为 swap(每次代价为a),第二种操作为 shuffle(每次代价为b)
考虑只用 swap 操作的子问题,对于任给定的排列,至少要 swap 几次?
- 答案是: 轮换环个数。
轮换环个数是啥
不理解的手动画图理解下
对于一个给定排列 ,考虑一个结点编号为 的有向图,对 ,有一条 指向 的有向边。
由于每个点入度出度均为 ,共 个点 条边,该有向图实际上是若干个不相交的简单环的并,这些环的数量就称为此排列的轮换环数。
感性证明:
末状态:排列已经升序,此时恰有 n 个一元环(每个点自己指向自己),需要 0 次操作。
(动手模拟下 swap , 对有向图的影响)如果i,j这两个点原先在同一个环上,那这个环就被切开,成了两个长度之和等于原环的更小的非空环;如果这两个点原先不在一个环上,这两个环就会合并成为一个长度等于原先两环之和的更大的环。
换句话说,每次 swap 可以将任一个长度>1的环随便切一刀变成两半,或者将任意两个小环合并成更大的环,而目标是将环切成 n 个,贪心地一直切,最小 swap 次数就是 n - 初状态环数
可 计算出只 swap 初始排列所需要的最小代价。
加入 shuffle 操作后,该怎么安排这两种操作?
由于 shuffle 是均匀随机地打乱这个序列,若进行了若干次 swap 后再 shuffle 一次,那前功尽弃,则最优的策略必是先进行若干(可以为0)次shuffle,再进行若干次(可以为0)swap,直到该排列达到目标状态。
如果当前的排列足够合适(环数比较多,即需要的 swap 次数比较少),那直接开始 swap 到结束;否则就再 shuffle 一次。
可以猜测该式成立:
再细想,我们希望的期望应该是需要收敛到一个min值的,不论已经 shuffle 了几次,每一次策略选择存在一个固定不变的“分界”,对于期望的计算也应该与已经进行了几次 shuffle 无关,考虑枚举这个“分界”:
存在整数 ,若当前环数 ,则直接 swap,否则继续 shuffle。对每一个 分别计算该策略的总代价的期望,取其中的最小值。
具体地,对于固定的一个 ,
设 为对于随机的排列,需要的代价期望;
设 为对于随机的排列,环数>=t 的概率;
设 为若当前排列环数>=t,需要的 swap 次数的期望,列式:
- 即
最后答案为
其中 和 的计算需要预处理有多少个 元排列恰有 个轮换环,记为 ,实际上这是第一类斯特林数,不过不熟悉的话也不难写出 的DP:
类似于经典问题“带标号连通图计数”,要计算 ,考虑 号点和哪些点联通,
-
号点成自环,这种情况方案数是 ;
-
号点不成自环,那就是在“n-1个点,m个环”的图中选一个位置插入,有n-1个位置可以插,这种情况方案数是
边界条件:
由于 n 元排列总数最多有 个,这样算不会爆long long。
一点小问题
基于枚举 取然后取最值的做法考虑用浮点数来比较大小,再精确计算模意义下答案,是否可以加强到 或者更大?
若能根据 直接计算合适的 ,输出模NTT质数的答案,是否可以加强到 的数量级?
B. BSUIR Open X
对 "BSUIROPENX"
,检查下它的每种前后缀在输入的那些字符串中出现了多少次,根据乘法原理和加法原理统计下答案。数据范围很小,用 map<string,int>
来索引下就行。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const string str = "BSUIROPENX";
int n;
map<string, int> mp;
signed main() {
ios::sync_with_stdio(0);cin.tie(0);
cin >> n;
for (int i = 1; i <= n; ++i) {
string si;
cin >> si;
++mp[si];
}
LL ans = 0;
for (int i = 1; i < str.size(); ++i) {
int u = mp[string(str.begin(), str.begin() + i)];
int v = mp[string(str.begin() + i, str.end())];
ans += (LL)u * v;
}
cout << ans << '\n';
return 0;
}
F. Function analysis
题意有点绕。。。翻译成人话就是:
从 的整数中有放回地等概率随机抽取 次,问:抽到的 个数排升序后,第 个数大于 的概率?
给定 ,对 输出答案,模
首先转化下题意:
第 个数大于 不超过 的数的数量不超过 个
于是就变成了高中数学概率题。。。由熟知的二项式与概率,
根据
则
变形为
计算 和 的卷积就行。
I. Items in boxes
据乘法原理,答案是
赛时思路
首先特判掉 的情形,得到条件
然后看下 是不是偶数,因此时 , 是偶数的话直接输出
是奇数的话,这时样例是输出 的,想了下模 这么大的数,余数太大的话甚至输出就T了,盲猜余数只能是 ,就A了
只证明最后一种情形
(归纳)对于 为奇数,,要证
(1)对于 ,设 ,,
则
由于 和 必为一奇一偶,则 是 的倍数,
即
(2)假设 结论成立,即 使得
则 ,
由于 ,
则 成立 (证毕)
J. Jenga(队友做的 stO zzwtx)
- 特判 n=1
- 关于 的递推数列要推对(我不会QAQ)
- 相互依赖的几个线性递推数列也可以矩阵快速幂,矩阵多开几行就好了
K. K-ones xor
先考虑一个简化版问题
构造一个x,使得 最大,要求 x 的二进制表示中 1 的个数不超过给定的k,若有多组解输出 x 最小的解
比较套路的按位讨论贪心题。可行的复杂度 ,其中 w 是数的位数。
回到原题,但是不好像上面那样做,只看 的其中一个数位的话,难以确定它对于特定的 到底有无贡献,于是再观察下 这个条件——怎样的 会使得 呢?
还是按二进制位去考虑(不妨设 )。因为整数的大小比较是从二进制高位开始到低位,我们也从高位到低位观察下每一位的 和 :
从最高位开始 如果有一段 (当然也可能没有),这些位置上 的值不变,这几位对答案没有影响;
则不妨考虑 的最高位的 1:
- 如果 在这一位上是 ,那 xor 完后会变成 ,
考虑到这个等比数列:1,2,4,8...,它任意位置的前缀部分和一定严格小于下一个位置的数,
这说明,如果 在这个位置上亏了一个 ,那在更低的数位上补回来再多的 都还是亏的,所以这个 是绝对不能亏的,换句话说,此时已经可以确定了(不管它们更低位长啥样), 不能 xor
- 如果 在这一位上是 ,那 xor 完后会变成 ,
同理,如果在这一位赚了个 ,在更低的数位上不管怎么亏,总体都是赚的,换句话说,此时已经可以确定必须让 xor
即:如果 的最高的 的位置确定了,那 中哪些数要和 异或,哪些不和 异或也都确定了
于是先枚举 的最高位的 ,假设是第 位,对应的数值为 ,,
将此时参与异或的那些 a[] 单独拿出,就是在解决开头所说的简化版问题:计算数位 对它们的贡献,取其中 个贡献最大(贡献相同时,数位低的优先级更高)且为贡献 的数位,就是这个 的答案
时间复杂度 ,实现需要注意细节
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· winform 绘制太阳,地球,月球 运作规律
· 上周热点回顾(3.3-3.9)