5/19/2024 测试

总结

  1. P 都不是,(本人)成绩构思依托(10pts)。
  2. 有重题,而且是同一场比赛出现两道 一模一样 的题(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 的数据范围更小)

结尾

一场有难度又离谱的测试,被拉爆了。

posted @ 2024-05-19 21:18  Foiled  阅读(12)  评论(0编辑  收藏  举报