C. Giant Domino
首先如果 \(S_N \leqslant 2S_1\),那么只需要取出首尾两张骨牌即可
下面考虑一般情况
考虑将序列 \(S\) 做升序排序,位于 \(1\) 号骨牌前和 \(N\) 号骨牌后的骨牌都不需要再考虑了
取出 \(1\) 号骨牌和 \(N\) 号骨牌之间的所有骨牌,并保留原来的骨牌之间的相对顺序,对于相邻两张骨牌一定得满足 \(S_{i+1} \leqslant 2S_i\),否则无解
然后用二分来找不超过上次选的数的 \(2\) 倍的数中的最大值的位置,时间复杂度为 \(\mathcal{O}(N\log N)\)
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
int solve() {
int n;
cin >> n;
vector<int> s(n);
rep(i, n) cin >> s[i];
if (s.back() <= s[0]*2) return 2;
{
vector<int> ns;
for (int x : s) {
if (x < s[0]) continue;
if (x > s.back()) continue;
ns.push_back(x);
}
s = ns;
n = s.size();
ranges::sort(s);
}
rep(i, n-1) {
if (s[i]*2 < s[i+1]) return -1;
}
int last = 0, ans = 2;
while (s[last]*2 < s.back()) {
last = upper_bound(s.begin(), s.end(), s[last]*2)-s.begin()-1;
ans++;
}
return ans;
}
int main() {
int t;
cin >> t;
while (t--) cout << solve() << '\n';
return 0;
}
D. Make 2-Regular Graph
枚举环排列 \(P\)
操作次数就是 $N+M-2 \times $ 保留下来的边数 \(C\)
其中 \(M-C\) 表示需要删掉的边数,\(N-C\) 表示需要添加的边数
考虑将下标 \(i\) 向 \(P_i\) 连一条有向边,会得到一颗基环树森林,且每个连通分量都是一个环,我们需要判掉出现自环和二元环的排列
时间复杂度为 \(\mathcal{O}(N!N)\)
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
int main() {
int n, m;
cin >> n >> m;
vector g(n, vector<int>(n));
rep(i, m) {
int a, b;
cin >> a >> b;
--a; --b;
g[a][b] = g[b][a] = 1;
}
int ans = 999;
vector<int> p(n);
rep(i, n) p[i] = i;
do {
bool ok = true;
rep(i, n) {
if (p[i] == i) ok = false;
if (p[p[i]] == i) ok = false;
}
if (ok) {
int now = 0;
rep(i, n) now += g[i][p[i]];
ans = min(ans, n+m-now*2);
}
} while (next_permutation(p.begin(), p.end()));
cout << ans << '\n';
return 0;
}
E. LCM Sequence
显然序列 \(A\) 一定单调不减
注意到 \(A_i \neq A_{i-1}\) 当且仅当 \(i\) 可以分解成素数的幂次
那么答案就是 \([L+1, R]\) 中可分解为素数幂次的数的个数 \(+1\)
跑一遍区间筛求出 \([L+1, R]\) 中每个数的素因子个数即可,这里只需要用到 \(1e7\) 以内的质数
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using ll = long long;
int main() {
ll l, r;
cin >> l >> r;
++l;
ll w = r-l+1;
vector<ll> x(w), cnt(w);
rep(i, w) x[i] = l+i;
const int M = 1e7;
vector<bool> isp(M, true);
isp[0] = isp[1] = false;
for (int i = 2; i < M; ++i) {
if (!isp[i]) continue;
for (int j = i+i; j < M; j += i) isp[j] = false;
for (ll j = (l+i-1)/i*i; j <= r; j += i) {
while (x[j-l]%i == 0) x[j-l] /= i;
cnt[j-l]++;
}
}
int ans = 1;
rep(i, w) {
if (x[i] != 1) cnt[i]++;
if (cnt[i] == 1) ans++;
}
cout << ans << '\n';
return 0;
}
F. Socks 4
当两只袜子颜色不同时,保留数量较多的一只更优 \(\to\) 因此持有的袜子颜色会逐渐向数量多的颜色演化
记 dp[i] 表示从持有颜色 \(i\) 袜子开始时的期望值
转移方程:
令 \(S = \sum A_i\)
\( \begin{aligned} dp[i] &= \dfrac{1}{S}\left(\Big(\sum_{j < i} A_j\Big) dp[i] + (A_i-1) \times 0 + \Big(\sum_{j > i} A_j dp[j]\Big)\right) + 1\\ &= \dfrac{\sum\limits_{j < i} A_j}{S} dp[i] + \dfrac{\sum\limits_{j > i} A_j dp[j]}{S} + 1 \end{aligned} \)
\( \left(1 - \dfrac{\sum\limits_{j < i} A_j}{S}\right)dp[i] = \dfrac{\sum\limits_{j > i} A_j dp[j]}{S} + 1 \)
\( dp[i] = \dfrac{\dfrac{\sum\limits_{j > i} A_j dp[j]}{S} + 1}{1 - \dfrac{\sum\limits_{j < i} A_j}{S}} = \dfrac{\Big(\sum\limits_{j > i} A_j dp[j]\Big) + S}{S-\sum\limits_{j < i} A_j} = \dfrac{\Big(\sum\limits_{j > i} A_j dp[j]\Big) + S}{\Big(\sum\limits_{j \geqslant i} A_j\Big) - 1} \)
对于 \(\Big(\sum\limits_{j \geqslant i} A_j\Big) - 1\) 和 \(\sum\limits_{j > i} A_j dp[j]\) 这两部分,可以用前缀和来加速
代码实现
#include <bits/stdc++.h>
#include <atcoder/all>
using namespace atcoder;
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using mint = modint998244353;
int main() {
int n, c;
cin >> n >> c; --c;
vector<int> a(n);
rep(i, n) cin >> a[i];
int s = 0;
rep(i, n) s += a[i];
a[c]++;
{
int na = a[c];
ranges::sort(a);
rep(i, n) if (a[i] == na) c = i;
}
vector<mint> dp(n);
mint suma = -1, sumx = 0;
for (int i = n-1; i >= 0; --i) {
suma += a[i];
dp[i] = (sumx+s)/suma;
sumx += dp[i]*a[i];
}
cout << dp[c].val() << '\n';
return 0;
}
G. Degree Harmony
带花树
浙公网安备 33010602011771号