5/19/2024 测试
总结
- P 都不是,(本人)成绩构思依托(10pts)。
- 有重题,而且是同一场比赛出现两道 一模一样 的题(T1 && T4)
题目 & 题解
T1 Color
Description
有 \(n\) 个球排成一列,每个球都有一个颜色,用 \(A \sim Z\) 的大写字母来表示,我们每次随机选出两个球 \(b_1,b_2\),使得后者染上前者的颜色,求期望操作多少次,才能使得所有球的颜色都一样?
Solution
%%%wzw's solution: link
简述整体思路。
已知 \(A\) 颜色共 \(i\) 个,那么将其它所有颜色变为 \(A\) 颜色的情况,当且仅当元素 \(j\) 为 \(A\) 颜色并选中它时,\(j - 1\) 或 \(j + 1\) 会改变颜色,剩余的变化对于将所有颜色变为 \(A\) 颜色都是无意义的。
接下来根据题意推式子,过程比较复杂,推荐看链接。
Code
#include <bits/stdc++.h>
using namespace std;
namespace Color {
constexpr int maxn = 10005;
int len;
string bal;
int num[30];
double ans = 0.0;
double f[maxn], f1[maxn], f2[maxn];
void solve() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> bal;
len = bal.size();
f1[1] = 1;
for (int i = 1; i <= len; i++) {
f1[i + 1] = (f1[i] - (i - 1) / 2.0 / i * f1[i - 1]) * 2.0 * i / (i + 1);
f2[i + 1] = (f2[i] - (i - 1) / 2.0 / i * f2[i - 1] - len * (len - 1) / 2.0 / i / (len - i)) * 2.0 * i / (i + 1);
}
f[1] = -f2[len] / f1[len];
for (int i = 1; i < len; i++) f[i] = f1[i] * f[1] + f2[i];
for (int i = 0; i < len; i++) num[bal[i] - 'A']++;
for (int i = 0; i < 26; i++) ans += f[num[i]] * num[i] / len;
cout << fixed << setprecision(1) << ans << '\n';
}
}
int main() {
Color::solve();
return 0;
}
Fun
笑点解析:我比赛时把初始化的 f1[1] = 1
放在了第一个 for
循环后,果断 RE 爆零。
T2 SSY的队列
Description
SSY 是班集体育委员,总喜欢把班级同学排成各种奇怪的队形,现在班级里有 \(N\) 个身高互不相同的同学,请你求出这 \(N\) 个人的所有排列中任意两个相邻同学的身高差均不为给定整数 \(M\) 的倍数的排列总数。
Solution
题意很简单,不需要解释。
70pts
注意到 \(70\%\) 的数据范围是 \(N \le 15\),可以考虑状压 DP。
设 \(dp_{i, s}\) 表示第 \(i\) 个位置时状态为 \(s\),直接进化成经典状压。
可以通过如下手段完成:
for (int i = 2; i <= n; i++) {
for (int l = 0; l < st[i - 1].size(); l++) {
int s = st[i - 1][l];
for (int j = 1; j <= n; j++) {
int S = 1 << (j - 1);
if ((s | S) == s) continue;
for (int k = 1; k <= n; k++) {
int ck = 1 << (k - 1);
if ((ck | s) != s) continue;
if (abs(a[j] - a[k]) % m == 0) continue;
dp[j][s | S] = (dp[j][s | S] + dp[k][s]) % p;
}
}
}
}
Code
完整代码(from @wzw0611):
#include<bits/stdc++.h>
#define MAXN 16
#define MAXM 1005
#define N (1<<15)+5
#define int long long
#define lowbit(p) p&(-p)
using namespace std;
const int p = 1234567891;
int n, m;
int a[MAXN];
int dp[MAXN][N], ans;
vector<int>st[MAXN];
signed main() {
scanf("%lld", &n);
for (int i = 1; i <= n; i++)scanf("%lld", &a[i]);
scanf("%lld", &m);
int toT = (1 << n) - 1;
for (int i = 0; i <= toT; i++) {
int tmp = i, cnt = 0;
while (tmp) {
++cnt;
tmp -= lowbit(tmp);
}
st[cnt].push_back(i);
}
for (int i = 1; i <= n; i++) {
int s = 1 << (i - 1);
dp[i][s] = 1;
}
//printf("ced\n");
for (int i = 2; i <= n; i++) {
for (int l = 0; l < st[i - 1].size(); l++) {
int s = st[i - 1][l];
// printf("i=%lld,s=%lld\n",i,s);
for (int j = 1; j <= n; j++) {
int S = 1 << (j - 1);
if ((s | S) == s)continue;
for (int k = 1; k <= n; k++) {
int ck = 1 << (k - 1);
if ((ck | s) != s)continue;
if (abs(a[j] - a[k]) % m == 0)continue;
dp[j][s | S] = (dp[j][s | S] + dp[k][s]) % p;
}
}
}
}
for (int i = 1; i <= n; i++)ans = (ans + dp[i][toT]) % p;
printf("%lld", ans);
return 0;
}
By the way, wzw got the first prize in this test!
100pts
还是注意数据范围。保证所有 \(N \le 30\),考虑搜索。
折半搜索是不可取的(\(\frac{30}{2}! \le 1e8\))。考虑记忆化搜索。
可以对输入的每一个元素对 \(M\) 取模,明显此时大小相等的元素相减一定可以被 \(M\) 整除,将他们分为一组,然后 dfs 记忆化搜索即可。这里使用 map
存储。
Code
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
using namespace std;
namespace SSY {
const int maxn = 35, base = 13331, mod = 1234567891;
int n, m;
int h[maxn], vis[maxn];
int num[maxn], tot;
int nowl[maxn];
int maxx, ans = 1, qua[33];
unordered_map<ull, int> mp[33];
int dfs(int x, int last) {
if (x > n) return 1;
memset(nowl, 0, sizeof(nowl));
for (int i = 1; i <= tot; i++)
if (i != last) nowl[num[i]]++;
ull ret = num[0];
for (int i = 1; i <= maxx; i++)
ret = ret * base + nowl[i];
ret = ret * base + num[last];
if (mp[x].find(ret) != mp[x].end()) return mp[x][ret];
int res = 0;
if (num[0]) {
num[0]--;
res = (res + dfs(x + 1, 0)) % mod;
num[0]++;
}
for (int i = 1; i <= tot; i++) {
if ((i != last) && num[i]) {
num[i]--;
res = (res + dfs(x + 1, i)) % mod;
num[i]++;
}
}
mp[x][ret] = res;
return res;
}
void get_qua() {
qua[0] = 1;
for (int i = 1; i <= maxx; i++)
qua[i] = qua[i - 1] * i % mod;
}
void solve() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n;
for (int i = 1; i <= n; i++) cin >> h[i];
cin >> m;
for (int i = 1; i <= n; i++)
h[i] = (h[i] % m + m) % m;
for (int i = 1; i <= n; i++) {
if (vis[i]) continue;
vis[i] = 1;
int res = 0;
for (int j = i; j <= n; j++) {
if (h[i] == h[j]) {
vis[j] = 1;
res++;
}
}
maxx = max(maxx, res);
if (res == 1) num[0]++;
else num[++tot] = res;
}
maxx = max(maxx, num[0]);
get_qua();
for (int i = 0; i <= tot; i++)
ans = ans * qua[num[i]] % mod;
ans = ans * dfs(1, 0) % mod;
cout << ans << '\n';
}
}
signed main() {
SSY::solve();
return 0;
}
% 场切 T2 的 dalao @Baiyj
T3 [JXOI2018] 游戏
九条可怜是一个富有的女孩子。
Description
她长大以后创业了,开了一个公司。 但是管理公司是一个很累人的活,员工们经常背着可怜偷懒,可怜需要时不时对办公室进行检查。
可怜公司有 \(n\) 个办公室,办公室编号是 \(l + n − 1\),可怜会事先制定一个顺序,按照这个顺序依次检查办公室。一开始的时候,所有办公室的员工都在偷懒,当她检查完编号是 \(i\) 的办公室时候,这个办公室的员工会认真工作,并且这个办公室的员工通知所有办公室编号是 \(i\) 的倍数的办公室,通知他们老板来了,让他们认真工作。因此,可怜检查完第 \(i\) 个办公室的时候,所有编号是 \(i\) 的倍数(包括 \(i\))的办公室的员工会认真工作。
可怜发现了员工们通风报信的行为,她发现,对于每种不同的顺序 \(p\),都存在一个最小的 \(t(p)\),使得可怜按照这个顺序检查完前 \(t(p)\) 个办公室之后,所有的办公室都会开始认真工作。她把这个 \(t(p)\) 定义为 \(p\) 的检查时间。
可怜想知道所有 \(t(p)\) 的和。
但是这个结果可能很大,她想知道和对 \(10^9 + 7\) 取模后的结果。
Solution
题面乱七八糟,简单来说就是给定 \(l\) 和 \(r\),对这其中的一些元素进行检查,当检查到 \(i\) 时,\(\forall_{t \le 1} \times i\) 的元素都会由未标记状态变为标记状态(初始时均为未标记状态)。
不妨举一个例子:
数组 \(P\) 由元素 \(1, 2, 3, 4, 5, 6, 7, 8, 9\) 组成,如果想标记所有的元素,尝试后可以发现,有一些元素是必须检查的(否则它不会被标记),而另一些元素可以通过检查别的元素来标记。比如说 \(P\) 中的 \(1, 2, 3, 5, 7\) 就必须检查。
看看,有没有发现什么?这些必须检查的元素(除了 \(1\))都是素数。这道题也就解决了。
Code
#include<bits/stdc++.h>
#define int long long
using namespace std;
namespace jtkl {
constexpr int INF = 0x3f3f3f3f, mod = 1e9 + 7, maxn = 1e7 + 5;
int l, r, cnt, ans;
bool npri[maxn];
void get_pri() {
for (int i = l; i <= r; i++) {
if (!npri[i]) {
cnt++, ans++;
for (int j = i << 1; j <= r; j += i) npri[j] = 1;
}
}
}
void solve() {
cin >> l >> r;
get_pri();
for (int i = 1; i <= r - l + 2; i++)
if (i != cnt + 1) ans = ans * i % mod;
cout << ans << '\n';
}
}
signed main() {
jtkl::solve();
return 0;
}
Fun
笑点解析:int j = i / 2
是错的。
T4 [MtOI2019] 小铃的烦恼
Description
小铃每天都会整理一次铃奈庵的书籍。这次桌子上有 \(n\) 本魔法书,这些书一次排成一排,每本书有一个魔法属性和编号。
最开始这些书的魔法属性都是一样的,但是因为被人多次使用,魔法属性发生了变化,小铃想让所有书的魔法属性重新全部相同。
这次小铃找到了雾雨 魔理沙(Kirisame Marisa)帮忙整理书籍,每次魔理沙可以释放选定魔法,魔法会随机选择两本书 \(a,b\) ( \(a\) 不等于 \(b\) )。
选定这两本书后,魔理沙会释放转移魔法,使得有 \(p_{a,b}\ (p_{a,b}\in (0,1])\) 的概率,第 \(b\) 本书的魔法属性变成第 \(a\) 本书的魔法属性。也就是说有 \(1-p_{a,b}\) 的概率,使得你即使选定了 \(a,b\) 两本书,但是魔法属性的转移不成功,意味着这次操作是无效的 。
注意 \(p_{a,b}\) 是对于转移是否成功的概率,和随机选择两本书的操作互不影响。
现在小铃想知道,求期望操作多少次,才能使所有的书魔法属性都一样?由于时间紧迫,小铃找到了你,希望你可以帮其解决这个问题,不然小铃就不会给你这题的分了。
Solution
和 T1 一模一样。
直接笑点解析:对于所有数据,满足 \(\left(\sum\limits_{a=1}^{n}\sum\limits_{b=1}^{n}p_{a,b}\right) = n^2\) 。
这相当于告诉你,输入中的矩阵,所有元素只有 \(1\)。鉴于没有多测,T1 的代码直接提交即可 AC。(甚至比 T1 的数据范围更小)
结尾
一场有难度又离谱的测试,被拉爆了。
Blog by cloud_eve is licensed under CC BY-NC-SA 4.0