比赛链接
Solved: 7/7
Rank: 42
A. Olympiad Date
题意:给一个数码序列,从前往后取,问取到第几个时凑齐 01032025 这八个数码。
开个桶统计。
void solve(){int n; cin >> n;vector<int> a(n); cin >> a;vector<int> c(10);for (int i=0; i<n; ++i){++c[a[i]];if (c[0] >= 3 && c[1] >= 1 && c[2] >= 2 && c[3] >= 1 && c[5] >= 1){cout << i+1 << '\n';return;}}cout << "0\n";
}
B. Team Training
题意:给一个集合,将其划分为不交子集,定义一个集合的权值为它的大小乘它的最小值。问最多可以划分出几个权值 \(\geq x\) 的子集。
从大到小排序,然后枚举,每次从当前的数开始贪心选最小的权值 \(\geq x\) 的子集。
void solve(){int n, x; cin >> n >> x;vector<int> a(n); cin >> a;sort(all(a));ll m = 0;int ans = 0;for (int i=n-1, j=0; i>=0; --i){++j;m = 1ll * j * a[i];if (m >= x) j=0, ++ans;}cout << ans << "\n";
}
C. Combination Lock
题意:构造一个长度为 \(n\) 的排列,使其每个轮换都恰有一个位置满足 \(p_i = i\)。
偶数无解,奇数 \(1,3,\dots,n,2,4,\dots,n-1\)。
void solve(){int n; cin >> n;if (n&1){for (int i=1; i<=n; i+=2) cout << i << ' ';for (int i=2; i<=n; i+=2) cout << i << ' ';cout << '\n';}else cout << "-1\n";
}
D. Place of the Olympiad
题意:给一个 \(n\times m\) 的网格黑白染色,使得恰好有 \(k\) 个黑格子,且同一行连续黑格子数的最大值最小。
行之间是独立的,所以每行黑格子数量相等一定更优。
如果一行连续黑格子数量不超过 \(d\),则总的黑格子数最多是 \(\lfloor \frac m{d+1}\rfloor\cdot d + (m\bmod (d+1))\)。
二分 \(d\) 即可(应该也可以直接算,懒得推了)。
void solve(){int n, m, k;cin >> n >> m >> k;int q = (k+n-1) / n;int l = 1, r = m, ans = 0;while (l <= r){int mid = (l+r) >> 1;if (q <= (m/(mid+1)) * mid + (m%(mid+1))) ans = mid, r = mid-1;else l = mid+1;}cout << ans << '\n';
}
E. Interesting Ratio
题意:给 \(n\),求满足 \(\frac{\text{lcm}(a,b)}{\gcd(a,b)}\) 为质数的 \((a,b)\) 无序对数。\(n\leq 10^7\)。
题目的条件等价于 \(b = pa\),\(p\) 为质数。
const int N = 1e7+5;
bool np[N];
int pri[N], cnt = 0, pi[N];
void init(int n) {for (int i=2; i<=n; ++i){if (!np[i]) pri[++cnt] = i;for (int j=1; j<=cnt && i*pri[j] <= n; ++j){np[i*pri[j]] = 1;if (!(i%pri[j])) break;}}for (int i=2; i<=n; ++i) pi[i] = pi[i-1] + !np[i];
}void solve(){int n; cin >> n;ll ans = 0;for (int i=1; i<=n; ++i) ans += pi[n/i];cout << ans << '\n';
}
F. Igor and Mountain
题意:给一个黑白网格。定义合法路径需要满足:只经过黑色格子,起点在最后一行终点在第一行,每行至少经过一个格子至多经过两个格子,路径上相邻两个格子距离不超过 \(d\)。求合法路径数量。
dp,设 \(f(i,j)\) 表示最后一个格子在 \(i\) 行 \(j\) 列的方案数。前缀和优化。
const int N = 2005;
int n, m, d;
string a[N];
ll f[N][N], g[N], h[N];void solve(){cin >> n >> m >> d;for (int i=1; i<=n; ++i) cin >> a[i], a[i] = " " + a[i];for (int i=n; i>=1; --i){for (int j=1; j<=m; ++j){g[j] = (g[j-1] + (a[i][j] == 'X' ? (i == n ? 1 : f[i+1][min(m, j+d-1)] - f[i+1][max(0, j-d)] + mod) : 0)) % mod;}for (int j=1; j<=m; ++j){h[j] = (h[j-1] + (a[i][j] == 'X' ? g[min(m, j+d)] - g[max(0, j-d-1)] + mod : 0)) % mod;}for (int j=1; j<=m; ++j) f[i][j] = h[j];}cout << f[1][m] << '\n';
}
G. Gleb and Boating
题意:数轴 \([0,s]\),你初始在 \(0\),要达到 \(s\)。你的初始速度为 \(v=k\),面朝正方向。每一步可以向面朝的方向走 \(v\) 的长度或者转向并令 \(v\) 减一(如果 \(v\) 已经是 \(1\) 就不减了),不能连续转向。问恰好到 \(s\) 时 \(v\) 的最大值。\(s\leq 10^9, k\leq 1000\)。
dp,设 \(f(i,j)\) 表示速度为 \(i\) 能达到的模 \(i\) 余 \(j\) 的最远位置。这里“最远”当面朝正向时表示最左端,面朝反向时表示最右端。则转移可以枚举速度是 \(i+1\) 时最后的位置和走的步数。步数只需要枚举到 \(i\) 因为后面的点是都可以达到的。
const int N = 1005;
int s, k, f[N][N];
void solve(){cin >> s >> k;for (int i=1; i<=k; ++i)for (int j=0; j<i; ++j){if ((k-i) & 1) f[i][j] = -1;else f[i][j] = s+1;}f[k][0] = k;for (int i=k-1; i>0; --i){if ((k-i) & 1){for (int j=0; j<=i; ++j) if (f[i+1][j] <= s){int R0 = f[i+1][j], R = R0 + (s - R0) / (i+1) * (i+1);for (int o=0; o<=i && R >= i && R >= R0; ++o, R -= i+1)f[i][R % i] = max(f[i][R % i], R - i);}}else{for (int j=0; j<=i; ++j) if (f[i+1][j] >= 0){int L0 = f[i+1][j], L = L0 - L0 / (i+1) * (i+1);for (int o=0; o<=i && L <= s-i && L <= L0; ++o, L += i+1)f[i][L % i] = min(f[i][L % i], L + i);}}}for (int i=k; i>0; i -= 2){for (int j=0; j<i; ++j)if (f[i][j] <= s && (s - f[i][j]) % i == 0){cout << i << '\n';return;}}cout << "1\n";
}