← 全部文章

Educational Codeforces Round 81 (Rated for Div. 2) 题解

本文目录(6 节)
  1. Display The Number
  2. Infinite Prefixes
  3. Obtain The String
  4. Same GCDs
  5. Permutation Separation
  6. Good Contest

Educational Codeforces Round 81 (Rated for Div. 2)

Display The Number

全 11 最优,如果 nn 为奇数则将最开始的 11 换成 77 即可。

const int N = 1e5 + 7;
int n;

inline void solve() {
	rd(n);
	if (n & 1) n -= 3, putchar('7');
	while (n) n -= 2, putchar('1');
	puts("");
}

int main() {
	int T;
	rd(T);
	while (T--) solve();
	return 0;
}

Infinite Prefixes

判掉 Σ=0\Sigma = 0 的情况(要么无解要么无限),枚举每个下标作为前缀最后一个数是否符合条件即可。

const int N = 1e5 + 7;
int n, m, a[N];
char s[N];

inline void solve() {
	rd(n), rd(m), rds(s, n);
	for (int i = 1; i <= n; i++) a[i] = a[i-1] + (s[i] == '0' ? 1 : -1);
	if (!a[n]) {
		int l = 0, r = 0;
		for (int i = 1; i <= n; i++)
			l = min(l, a[i]), r = max(r, a[i]);
		if (l <= m && m <= r) print(-1);
		else print(0);
		return;
	}
	int t = !m;
	for (int i = 1; i <= n; i++)
		if ((m - a[i]) % a[n] == 0 && (m - a[i]) / a[n] >= 0) ++t;
	print(t);
}

int main() {
	int T;
	rd(T);
	while (T--) solve();
	return 0;
}

Obtain The String

预处理每个位置往后的第一个 a∼z\texttt{a} \sim \texttt{z},用 tt 去模拟匹配即可。

const int N = 1e5 + 7;
int n, m, f[N][26], v[26];
char s[N], t[N];

inline void solve() {
	rds(s, n), rds(t, m);
	int ans = 1;
	for (int i = 0; i < 26; i++) v[i] = n + 1;
	for (int i = n; i; i--) {
		for (int j = 0; j < 26; j++) f[i][j] = v[j];
		v[s[i]-'a'] = i;
	}
	for (int i = 0; i < 26; i++) f[0][i] = v[i];
	for (int i = 1, j = 0; i <= m; i++) {
		int c = t[i] - 'a';
		if (f[j][c] != n + 1) j = f[j][c];
		else {
			++ans;
			if (f[0][c] == n + 1) return print(-1);
			j = f[0][c];
		}
	}
	print(ans);
}

int main() {
	int T;
	rd(T);
	while (T--) solve();
	return 0;
}

Same GCDs

ans=φ(mgcd⁡(n,m))ans = \varphi(\frac{m}{\gcd(n, m)})。

inline void solve() {
	ll n, m;
	rd(n), rd(m);
	ll d = __gcd(n, m);
	m /= d;
	ll ans = n = m;
	for (ll i = 2; i * i <= n; i++)
		if (n % i == 0) {
			ans = ans / i * (i - 1);
			while (n % i == 0) n /= i;
		}
	if (n > 1) ans = ans / n * (n - 1);
	print(ans);
}

int main() {
	int T;
	rd(T);
	while (T--) solve();
	return 0;
}

Permutation Separation

转化一下题意:

平面上有 nn 个点 (i,pi)(i, p_i),你需要找到一个点 (x,y)(x∈[1,n),y∈[0,n])(x,y)(x \in [1, n), y \in [0, n]),使得在这个点的严格右下和严格左上的点的权值和最小。

线段树维护区间加和全局最小值即可。

const int N = 2e5 + 7;
int n, a[N], b[N], p[N];
ll s[N];
struct T {
	int l, r;
	ll x, z;
} t[N*4];

void build(int p, int l, int r) {
	t[p].l = l, t[p].r = r;
	if (l == r) return t[p].x = s[l], void();
	build(ls, l, md), build(rs, md + 1, r);
	t[p].x = min(t[ls].x, t[rs].x);
}

void spd(int p) {
	if (t[p].z) {
		t[ls].x += t[p].z;
		t[ls].z += t[p].z;
		t[rs].x += t[p].z;
		t[rs].z += t[p].z;
		t[p].z = 0;
	}
}

void add(int p, int l, int r, int k) {
	if (t[p].l >= l && t[p].r <= r) return t[p].x += k, t[p].z += k, void();
	spd(p);
	if (l <= md) add(ls, l, r, k);
	if (r > md) add(rs, l, r, k);
	t[p].x = min(t[ls].x, t[rs].x);
}

int main() {
	rd(n);
	for (int i = 1; i <= n; i++) rd(a[i]), p[a[i]] = i; 
	for (int i = 1; i <= n; i++) rd(b[i]), s[i] = s[i-1] + b[i];
	build(1, 1, n - 1);
	ll ans = t[1].x;
	for (int i = 1; i <= n; i++) {
		if (p[i] > 1) add(1, 1, p[i] - 1, b[p[i]]);
		if (p[i] < n) add(1, p[i], n - 1, -b[p[i]]);
		ans = min(ans, t[1].x);
	}
	print(ans);
	return 0;
}

Good Contest

题意即 aia_i 在 [li,ri][l_i, r_i] 等概率随机,求 a1…na_{1 \dots n} 不增的概率。

跟 P3643 [APIO2016]划艇 几乎一样:aia_i 在 [li,ri][l_i, r_i] 等概率随机,也可以跳过,求形成的序列递增的方案数。

先考虑这题。

首先将区间离散化成若干个左闭右开的区间。

设 fi,jf_{i, j} 表示考虑到 aia_i,aia_i 在第 jj 个区间内的方案数。

枚举上一个在 jj 之前的位置 aka_k 转移。

设 k+1∼ik + 1 \sim i 中有 cc 个位置可以选择第 jj 个区间,设第 jj 个区间的长度为 ljl_j,则方案数为 (lj+cc)\binom{l_j + c}{c}。

需要前缀和优化,时间复杂度 O(n3)\mathcal O(n^3)。

const int N = 507;
int n, a[N], b[N], c[N*2], t;
modint f[N], g[N], ans;

int main() {
	rd(n);
	for (int i = 1; i <= n; i++)
		rd(a[i]), rd(b[i]), c[++t] = a[i], c[++t] = ++b[i];
	sort(c + 1, c + t + 1), t = unique(c + 1, c + t + 1) - (c + 1);
	for (int i = 1; i <= n; i++)
		a[i] = lower_bound(c + 1, c + t + 1, a[i]) - c,
		b[i] = lower_bound(c + 1, c + t + 1, b[i]) - c;
	f[0] = 1;
	for (int j = 1; j < t; j++) {
		int l = c[j+1] - c[j];
		g[0] = 1;
		for (int i = 1; i <= n; i++) g[i] = g[i-1] * (l + i - 1) / i;
		for (int i = n; i; i--)
			if (a[i] <= j && j < b[i])
				for (int c = 1, k = i - 1; ~k; k--) {
					f[i] += g[c] * f[k];
					if (a[k] <= j && j < b[k]) ++c;
				}
	}
	for (int i = 1; i <= n; i++) ans += f[i];
	print(ans);
	return 0;
}

再来看原题就很简单了,照着做即可。

const int N = 57;
int n, a[N], b[N], c[N*2], t;
modint f[N], g[N], ans;

int main() {
	rd(n);
	for (int i = 1; i <= n; i++)
		rd(a[i]), rd(b[i]), c[++t] = a[i], c[++t] = ++b[i];
	sort(c + 1, c + t + 1), t = unique(c + 1, c + t + 1) - (c + 1);
	for (int i = 1; i <= n; i++)
		a[i] = lower_bound(c + 1, c + t + 1, a[i]) - c,
		b[i] = lower_bound(c + 1, c + t + 1, b[i]) - c;
	f[0] = 1;
	for (int j = t - 1; j; j--) {
		int l = c[j+1] - c[j];
		g[0] = 1;
		for (int i = 1; i <= n; i++) g[i] = g[i-1] * (l + i - 1) / i;
		for (int i = n; i; i--)
			if (a[i] <= j && j < b[i])
				for (int c = 1, k = i - 1; ~k; k--, ++c) {
					f[i] += g[c] * f[k];
					if (a[k] > j || j >= b[k]) break;
				}
	}
	ans = f[n];
	for (int i = 1; i <= n; i++) ans /= c[b[i]] - c[a[i]];
	print(ans);
	return 0;
}

标签:DP、数论、概率期望、线段树、贪心

评论

    填写昵称即可留言。评论经站主审核后公开,请勿提交私人信息。

    正在连接评论服务…