其他分享
首页 > 其他分享> > Petrozavodsk Summer 2022. Day 1. Welcome Contest

Petrozavodsk Summer 2022. Day 1. Welcome Contest

作者:互联网

Petrozavodsk Summer 2022. Day 1. Welcome Contest

是不是又开新坑了,毛营我来了!

挑几道自己会的 & 有意思 的题写题解 QwQ

D - Double Sort

首先最后前缀和的过程可以省略,直接求每个位置差分的期望值,最后加回来即可。

直接设 \(f(i,j,k)\) 表示:序列长度为 \(i\),值域为 \([1,j]\) 时 \(a'_{k+1}-a'_{k}\) 的期望值,那么 \(\text{ans}_i=\sum_{j=0}^{i-1} f(n,m,j)\)。

因为将差分排序了,转移不妨将所有差分 \(-1\),并将所有大小为 \(1\) 的差分都去掉,来归入值域更小的子问题。

考虑枚举大小为 \(1\) 的差分个数 \(l\),不难得到递推式:

\[f(i,j,k)=\frac{1}{\binom{j}{i}}\sum_{l=0}^{i} \binom{i}{l}\binom{j-i}{i-l} ([l<k](f(i-l,j-i,k-l)+1)+[l\geq k]) \]

直接计算就是 \(O(n^3m)\) 的,喜闻乐见的跑不满。

code
#include<bits/stdc++.h>
typedef long long ll;
#define rep(i, a, b) for(int i = (a); i <= (b); i ++)
#define per(i, a, b) for(int i = (a); i >= (b); i --)
#define Ede(i, u) for(int i = head[u]; i; i = e[i].nxt)
using namespace std;

inline int read() {
	int x = 0, f = 1; char c = getchar();
	while(c < '0' || c > '9') f = (c == '-') ? - 1 : 1, c = getchar();
	while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
	return x * f;
}

const int N = 55, M = 10010;
int n, m; double c[M][N], f[N][M][N];

void prework() {
	c[0][0] = 1.0;
	rep(i, 1, m) {
		c[i][0] = 1.0;
		rep(j, 1, min(i, n)) c[i][j] = c[i - 1][j] + c[i - 1][j - 1];
	}
}

int main() {
	n = read(), m = read();
	prework();
	rep(j, 1, m) rep(i, 1, min(n, j)) rep(x, 0, i) {
		double p = c[i][x] * c[j - i][i - x] / c[j][i];
		rep(k, 1, x)
			f[i][j][k] += p * 1.0;
		rep(k, x + 1, i)
			f[i][j][k] += p * (f[i - x][j - i][k - x] + 1.0);
	}
	double ans = 0.0;
	rep(i, 1, n) ans += f[n][m][i], printf("%.9f\n", ans);
	return 0;
}

F - Leaderboard Effect

最后一句话实际就是求每道题通过的概率。

一开始拿到这题发现条件繁多,看数据范围知道是状压,但完全不知道加权平均怎么决策。

实际上根据概率的性质直接维护就好,设 \(f(i,j)\) 表示在时间 \(i\) 题目 \(j\) 已经被通过的概率。

\(g(i,s)\) 表示在时间 \(i\) 开始新一轮决策,已经读过的题目集合为 \(s\) 的局面出现的概率。

转移直接转移就好,感觉很厉害。

code
#include<bits/stdc++.h>
typedef long long ll;
#define rep(i, a, b) for(int i = (a); i <= (b); i ++)
#define per(i, a, b) for(int i = (a); i >= (b); i --)
#define Ede(i, u) for(int i = head[u]; i; i = e[i].nxt)
using namespace std;

inline int read() {
	int x = 0, f = 1; char c = getchar();
	while(c < '0' || c > '9') f = (c == '-') ? - 1 : 1, c = getchar();
	while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
	return x * f;
}

const int N = 17, M = 110;
const double eps = 1e-10;
int n, m, r[N], c[N];
double p[N], f[M][N], g[M][1 << N];

int main() {
	n = read(), m = read();
	rep(i, 0, n - 1) r[i] = read(), c[i] = read(), scanf("%lf", &p[i]);
	g[0][0] = 1.0;
	rep(i, 0, m) {
		if(i) rep(o, 0, n - 1) f[i][o] += f[i - 1][o];
		rep(s, 0, (1 << n) - 1) {
			double sum = 0.0; int cnt = 0;
			rep(o, 0, n - 1) if(! (s >> o & 1)) sum += f[i][o], cnt ++;
			rep(o, 0, n - 1) if(! (s >> o & 1)) {
				double cur = (sum > eps) ? (f[i][o] / sum) : (1.0 / cnt);
				if(i + r[o] <= m)
					g[i + r[o]][s | (1 << o)] += cur * (1 - p[o]) * g[i][s];
				if(i + r[o] + c[o] <= m)
					f[i + r[o] + c[o]][o] += cur * p[o] * g[i][s], 
					g[i + r[o] + c[o]][s | (1 << o)] += cur * p[o] * g[i][s];
			}
		}
	}
	rep(i, 0, n - 1) printf("%.10f\n", f[m][i]);
	return 0;
}

H - Ranked Choice Spoiling

首先只有两个人是简单的,只有两种排行:AB 和 BA。

如果 A 本来就能赢那就赢了,否则第一轮就一定是要让 B 出局的,方法是将一些 BA 变为 ZBA。

当然同时不能让 Z 喧宾夺主了,不难计算发现极限情况是 AB : BA = 1 : 3。

对于三个的情况也是类似的。

首先容易证明 Z 只会放在开头或结尾。

然后枚举先排除 B 还是 C,并尽量少的放 Z 来排除掉他,以 B 为例。

一定是优先转变 BCA,因为 BAC 在 B 出局后就能给 A 贡献。之后考虑两种情况:

先排除 C 的决策是类似的,所以这题只是比较简单的贪心。但是这是赛时通过队伍最少的一道题?

code
#include<bits/stdc++.h>
typedef long long ll;
typedef unsigned long long ull;
#define rep(i, a, b) for(int i = (a); i <= (b); i ++)
#define per(i, a, b) for(int i = (a); i >= (b); i --)
#define Ede(i, u) for(int i = head[u]; i; i = e[i].nxt)
using namespace std;

inline int read() {
	int x = 0, f = 1; char c = getchar();
	while(c < '0' || c > '9') f = (c == '-') ? - 1 : 1, c = getchar();
	while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
	return x * f;
}

const int N = 1010;
int n, m, num[3], cnt[3], sta[6], cur[6];

bool solve0() {
	rep(i, 0, 2) cnt[i] = num[i];
	if(cnt[1] > n / 2 || cnt[2] > n / 2) return false;
	if(cnt[1] > cnt[0] && cnt[2] > cnt[0]) return false;
	if(cnt[2] <= cnt[1]) {
		cnt[0] += sta[4], cnt[1] += sta[5];
		return 3 * cnt[0] >= cnt[1];
	}
	else {
		cnt[0] += sta[2], cnt[2] += sta[3];
		return 3 * cnt[0] >= cnt[2];
	}
}

bool solve1() {
	if(! num[2]) return false;
	
	int now[4];
	rep(i, 0, 3) now[i] = 0;
	rep(i, 0, 5) cur[i] = sta[i];

	if(num[1]) {
		int aim = min(num[0], min(num[1], num[2]));
		if(num[2] == aim) aim --;
		int ned = num[1] - aim;
		
		if(cur[3] >= ned) cur[3] -= ned, now[3] += ned;
		else {
			ned -= cur[3], now[3] += cur[3], cur[3] = 0;
			cur[2] -= ned, now[2] += ned;
		}
	}
	
	now[0] = num[0] + cur[2];
	now[1] = num[2] + cur[3];
	
	int x = now[0], y = now[1], z = now[2] + now[3];
	if(y > x) z += y - x, y = x;
	while(z <= y) z ++, y --;
	if(x + y >= z) return true;
	
	x = now[0], y = now[1], z = now[2] + now[3];
	if(z <= x && z <= y && x + now[2] >= y + now[3]) return true;
	return false;
}

bool solve2() {
	int now[4];
	rep(i, 0, 3) now[i] = 0;
	rep(i, 0, 5) cur[i] = sta[i];
	
	if(num[2]) {
		int aim = min(num[0], min(num[1], num[2]));
		int ned = num[2] - aim;
		if(cur[5] >= ned) cur[5] -= ned, now[3] += ned;
		else {
			ned -= cur[5], now[3] += cur[5], cur[5] = 0;
			cur[4] -= ned, now[2] += ned; 
		}
	}
	
	now[0] = num[0] + cur[4];
	now[1] = num[1] + cur[5];
	
	int x = now[0], y = now[1], z = now[2] + now[3];
	if(y > x) z += y - x, y = x;
	while(z <= y) z ++, y --;
	if(x + y >= z) return true;
	
	x = now[0], y = now[1], z = now[2] + now[3];
	if(z <= x && z <= y && x + now[2] >= y + now[3]) return true;
	return false;
}

int main() {
	n = read(), m = read();

	if(m == 2) {
		rep(i, 1, n) {
			char a[3]; scanf("%s", a);
			char b[3]; scanf("%s", b);
			if(a[0] == 'A') cnt[0] ++; else cnt[1] ++;
		}
		puts(3 * cnt[0] >= cnt[1] ? "1" : "0");
		return 0;
	}

	rep(i, 1, n) {
		char a[3]; scanf("%s", a);
		char b[3]; scanf("%s", b);
		char c[3]; scanf("%s", c);
		num[a[0] - 'A'] ++;
		if(a[0] == 'A') sta[b[0] == 'B' ? 0 : 1] ++;
		else if(a[0] == 'B') sta[b[0] == 'A' ? 2 : 3] ++;
		else sta[b[0] == 'A' ? 4 : 5] ++;
	}
	
	if(solve0() || solve1() || solve2()) puts("1"); else puts("0");
	return 0;
}

K - Transparency

思考时间最长的一道题。猛然发现直接优化建图就好……

相当于两个点在自动机上走,首先将所有终止节点都连向一个虚点 \(z\)。

\(\text{same}(i)\) 表示到 \(i\) 都一模一样。

\(\text{diff}(i,j)\) 表示一个到了节点 \(i\) 一个到了节点 \(j\) 且一定不完全一样。

发现这样的转移是乏力的,因为缺少了从 \(\text{same}\) 开始一直总小写字母的转移。

这种转移时不能归入 \(\text{diff}\) 的,因为 \(\text{diff}\) 两个节点都能任意走小写字母,如果从 \(\text{same}\) 出发很可能错误的构造了一模一样的串。

解决方法是再设 \(\text{walk}(i,j)\) 表示一个到了节点 \(i\) 一个到了节点 \(j\),且节点 \(i\) 比节点 \(j\) 多走了若干小写字母的情况。

转移按照定义就好,时刻保证大写字母的子序列完全一致。之后最短路的起点就是 \(\text{same}(1)\),终点就是 \(\text{diff}(z,z)\)。

点数是 \(O(n^2)\) 的,边数再乘字符集大小,还是很宽松的。

code
#include<bits/stdc++.h>
typedef long long ll;
typedef unsigned long long ull;
#define rep(i, a, b) for(int i = (a); i <= (b); i ++)
#define per(i, a, b) for(int i = (a); i >= (b); i --)
#define Ede(i, u) for(int i = head[u]; i; i = e[i].nxt)
using namespace std;

#define eb emplace_back
typedef pair<int, int> pii;
#define mp make_pair
#define fi first
#define se second

inline int read() {
	int x = 0, f = 1; char c = getchar();
	while(c < '0' || c > '9') f = (c == '-') ? - 1 : 1, c = getchar();
	while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
	return x * f;
}

const int N = 55;
const int M = N * N * 3;
const int inf = 1e9;
int n, p, m, tr[N][60];
int tot, same[N], walk[N][N], diff[N][N];
int dis[M]; bool vis[M];
vector<pii> g[M];

void add(int u, int v, int w) {g[u].eb(mp(v, w));}

int main() {
	n = read() + 1, p = read(), m = read();
	rep(i, 1, p) {int x = read(); tr[x][0] = n;}
	rep(i, 1, m) {
		int x = read();
		char c[3]; scanf("%s", c);
		int y = read();
		int cur = (c[0] <= 'Z') ? (c[0] - 'A' + 1) : (c[0] - 'a' + 27);
		tr[x][cur] = y;
	}
	
	rep(i, 1, n) {
		same[i] = ++ tot;
		rep(j, 1, n) walk[i][j] = ++ tot, diff[i][j] = ++ tot;
	}
	
	rep(i, 1, n) {
		rep(c, 0, 52) if(tr[i][c]) add(same[i], same[tr[i][c]], 2);
		rep(c, 27, 52) if(tr[i][c]) add(same[i], walk[tr[i][c]][i], 1);
		rep(c1, 27, 52) rep(c2, 27, 52) if(tr[i][c1] && tr[i][c2] && c1 != c2)
			add(same[i], diff[tr[i][c1]][tr[i][c2]], 2);
	}
	
	rep(i, 1, n) rep(j, 1, n) {
		rep(c, 27, 52) if(tr[i][c])
			add(diff[i][j], diff[tr[i][c]][j], 1), 
			add(walk[i][j], walk[tr[i][c]][j], 1);
		rep(c, 27, 52) if(tr[j][c])
			add(diff[i][j], diff[i][tr[j][c]], 1);
		rep(c, 0, 26) if(tr[i][c] && tr[j][c])
			add(diff[i][j], diff[tr[i][c]][tr[j][c]], 2), 
			add(walk[i][j], diff[tr[i][c]][tr[j][c]], 2);
	}
	
	rep(i, 1, tot) dis[i] = inf;
	priority_queue<pii> q;
	int s = same[1]; q.push(mp(dis[s] = 0, s));
	while(! q.empty()) {
		int u = q.top().se; q.pop();
		if(vis[u]) continue; else vis[u] = true;
		for(pii e : g[u]) {
			int v = e.fi, w = e.se;
			if(dis[v] > dis[u] + w) dis[v] = dis[u] + w, q.push(mp(- dis[v], v));
		}
	}
	
	int ans = dis[diff[n][n]];
	printf("%d\n", ans == inf ? -1 : ans - 2); 
	
	return 0;
}

M - Word Ladder

WA 了 7 发终于写对各种细节的构造。

实际也比较简单,从 aaaaaaaaaa 出发,每次挑相邻的两个一直执行如下过程:

 aa -> ba -> bb -> cb -> cc -> ... -> zy -> zz

这样大概的总单词数就是 \(5\times 26\times 50\) 的,但是不同列的转换,以及不同底字母的转换有些细节,可能会牺牲一些单词。

最终造出的单词表大小为 \(6461\) QwQ

code
#include<bits/stdc++.h>
typedef long long ll;
typedef unsigned long long ull;
#define rep(i, a, b) for(int i = (a); i <= (b); i ++)
#define per(i, a, b) for(int i = (a); i >= (b); i --)
#define Ede(i, u) for(int i = head[u]; i; i = e[i].nxt)
using namespace std;

#define eb emplace_back

inline int read() {
	int x = 0, f = 1; char c = getchar();
	while(c < '0' || c > '9') f = (c == '-') ? - 1 : 1, c = getchar();
	while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
	return x * f;
}

vector<vector<char>> ans;

vector<char> solve(vector<char> s, int c, int p) {
	int cur = (c == 0 ? 1 : (c == 1 ? 2 : 0));
	rep(i, 1, 24 + (c == 0)) {
		s[p] = cur + 'a'; 	  if(! p || i > 1) ans.eb(s);
		s[p ^ 1] = cur + 'a'; if(! p || i > 1) ans.eb(s);
		cur ++;
		if(cur == c - 1) cur ++;
		if(cur == c) cur ++;
	}
	return s;
}

int main() {
	rep(c, 0, 24) {
		vector<char> sta(10, c + 'a'), las;
		if(c == 0) ans.eb(sta);
		rep(i, 0, 4) {
			las = solve(sta, c, i << 1);
			if(i < 4) {
				int nxt = (c == 0 ? 1 : (c == 1 ? 2 : 0));
				las[(i << 1) + 2] = nxt + 'a', ans.eb(las);
				las[(i << 1) + 3] = nxt + 'a', ans.eb(las);
				las[i << 1] = c + 'a', ans.eb(las);
				las[i << 1 | 1] = c + 'a', ans.eb(las);
			}
		}
		if(c < 25) {
			rep(j, 0, 2) las[j] = c + 1 + 'a', ans.eb(las);
			per(j, 9, 3) las[j] = c + 1 + 'a', ans.eb(las);
		}
	}
	int n = read();
	rep(i, 0, n - 1) {rep(j, 0, 9) putchar(ans[i][j]); puts("");}
	return 0;
}

标签:Summer,cnt,cur,Contest,int,rep,Welcome,num,now
来源: https://www.cnblogs.com/lpf-666/p/16676222.html