VP Educational Codeforces Round 21
A. Lucky Year
题意:找比\(n\)大的下一个只有一位数不是\(0\)的数。
先和\(n\)同位数,然后高位一直加一直到大于\(n\)。
点击查看代码
void solve() {
i64 n;
std::cin >> n;
i64 x = 1;
while (x * 10 <= n) {
x *= 10;
}
i64 m = x;
while (m <= n) {
m += x;
}
std::cout << m - n << "\n";
}
B. Average Sleep Time
题意:\(k\)个数一组,求所有组的和的平均数。
前缀和预处理即可。
点击查看代码
void solve() {
int n, k;
std::cin >> n >> k;
std::vector<int> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
std::vector<i64> sum(n + 1);
for (int i = 0; i < n; ++ i) {
sum[i + 1] = sum[i] + a[i];
}
double ans = 0;
for (int i = k; i <= n; ++ i) {
ans += sum[i] - sum[i - k];
}
std::cout << std::fixed << std::setprecision(12);
std::cout << ans / (n - k + 1) << "\n";
}
C. Tea Party
题意:构造一个长度为\(n\)的序列\(b\),使得\(\sum_{i=1}^{n} b_i = m\),\(\forall i \in [1, n], b_i \geq \lceil \frac{a_i}{2} \rceil\),\(\forall i, j \in [1, n] \ if \ a_i \geq a_j \ then \ b_i \geq a_j\)。
先满足\(b_i \geq \lceil \frac{a_i}{2} \rceil\),然后按照\(a_i\)从大到小放,能放多少就放多少。
点击查看代码
void solve() {
int n, m;
std::cin >> n >> m;
std::vector<std::pair<int, int>> a(n);
std::vector<int> ans(n);
for (int i = 0; i < n; ++ i) {
int x;
std::cin >> x;
a[i] = {x, i};
ans[i] = (x + 1) / 2;
m -= (x + 1) / 2;
}
if (m < 0) {
std::cout << -1 << "\n";
return;
}
std::sort(a.begin(), a.end(), std::greater<>());
for (auto & [x, i] : a) {
int v = std::min(m, x - (x + 1) / 2);
ans[i] += v;
m -= v;
}
for (int i = 0; i < n; ++ i) {
std::cout << ans[i] << " \n"[i == n - 1];
}
}
D. Array Division
题意:给你一个数组,移动一个数,使得数组的一个前缀的和与一个后缀的和相等。
只能移动一个,考虑移动哪一个很困难,可以直接枚举移动这个数影响的前缀和后缀,如果当前前缀为\(pre\),则如果\(pre > \frac{sum}{2}\)需要从前面找一个\(\frac{sum}{2} - pre\)移动后面,否则\(pre < \frac{sum}{2}\),需要从后面找一个\(\frac{sum}{2} - pre\)到前面。如果\(pre = \frac{sum}{2}\)就直接\(yes\)。用\(map\)记录两边的数。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::vector<i64> a(n);
std::map<i64, int> pre, suf;
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
++ suf[a[i]];
}
i64 sum = std::accumulate(a.begin(), a.end(), 0ll);
if (sum & 1) {
std::cout << "NO\n";
return;
}
i64 x = sum / 2;
sum = 0;
for (int i = 0; i < n; ++ i) {
sum += a[i];
pre[a[i]] += 1; suf[a[i]] -= 1;
if (sum == x) {
std::cout << "YES\n";
return;
} else if (sum < x && suf[x - sum] > 0) {
std::cout << "YES\n";
return;
} else if (sum > x && pre[sum - x] > 0) {
std::cout << "YES\n";
return;
}
}
std::cout << "NO\n";
}
E. Selling Souvenirs
题意:一个背包,\(n, m\)都是\(1e5\)级别,但物品的体积只有\(\{1, 2, 3\}\)三种。求最大价值。
以前没写过这种类似贪心的\(dp\)。
每个物品按体积分类,然后按价值从大到小排序,那么我们肯定是选每一类的一个前缀。我们先只管体积为\(1\)和\(2\)的。记\(f[i] = \{cost, cnt_1, cnt_2\}\),表示总体积为\(i\)时最大价值为\(cost\),且取到这个价值时选了\(cnt_1\)个\(1\),\(cnt_2\)个\(2\)。然后转移就显然,因为我们知道了选了几个\(1\)和几个\(2\),直接枚举拿一个\(1\),拿两个\(1\)和拿一个\(2\)的情况转移就行了。
我感觉这个记录选几个\(1\)和\(2\)的状态就比较贪心,不管仔细想一下,这其实就是\(f[i][j][k]\)表示总体积为\(i\)取到这个价值时选了\(j\)个\(1\),\(k\)个\(2\)时最大价值,压了两维,然后只记录最优方案,因为我们只需要最优解,其它的状态可以丢弃。感觉还是很奇妙,以前写\(dp\)都是一个状态一维,有时利用一些性质减掉几维,但确实没见过把状态和值放到一起的。
点击查看代码
void solve() {
int n, m;
std::cin >> n >> m;
std::vector<std::vector<i64>> a(4);
for (int i = 0; i < n; ++ i) {
int w, c;
std::cin >> w >> c;
a[w].push_back(c);
}
for (int i = 1; i <= 3; ++ i) {
std::sort(a[i].begin(), a[i].end(), std::greater<>());
while (a[i].size() <= 2 * m) {
a[i].push_back(0);
}
}
std::vector<std::array<i64, 3>> f(m + 2);
for (int i = 0; i < m; ++ i) {
if (f[i + 1][0] < f[i][0] + a[1][f[i][1]]) {
f[i + 1][0] = f[i][0] + a[1][f[i][1]];
f[i + 1][1] = f[i][1] + 1;
f[i + 1][2] = f[i][2];
}
if (f[i + 2][0] < f[i][0] + a[1][f[i][1]] + a[1][f[i][1] + 1]) {
f[i + 2][0] = f[i][0] + a[1][f[i][1]] + a[1][f[i][1] + 1];
f[i + 2][1] = f[i][1] + 2;
f[i + 2][2] = f[i][2];
}
if (f[i + 2][0] < f[i][0] + a[2][f[i][2]]) {
f[i + 2][0] = f[i][0] + a[2][f[i][2]];
f[i + 2][1] = f[i][1];
f[i + 2][2] = f[i][2] + 1;
}
}
i64 ans = 0, pre = 0;
for (int i = 0; i <= m / 3; ++ i) {
ans = std::max(ans, f[m - i * 3][0] + pre);
pre += a[3][i];
}
std::cout << ans << "\n";
}
F. Card Game
题意:有\(n\)个三元组\((p, c, l)\),如果你的等级大于等于\(l\)则可以选这个三元组,如果两个三元组的\(c_i + c_j\)是质数,则这两个不能同时选,选的三元组的价值就是\(p\)和,你想要和大于等于\(k\),问等级最少是多少。
我们把奇数放一起,偶数放一起,就变成了二部图,然后如果两个点的和是质数就连边,这样就变成了求最大独立集。
但\(1\)需要特殊处理,因为\(1+1\)是质数,但我们最多选一个1,于是我们保留\(p\)最大的那个1就行。
可以二分等级,也可以一级一级增加,然后把对应等级的三元组加进来和已有的连边。因为我们是在残留网络上跑增广路,复杂度是可以接受的。
点击查看代码
constexpr int inf = 1E9;
template<class T>
struct MaxFlow {
struct _Edge {
int to;
T cap;
_Edge(int to, T cap) : to(to), cap(cap) {}
};
int n;
std::vector<_Edge> e;
std::vector<std::vector<int>> g;
std::vector<int> cur, h;
MaxFlow() {}
MaxFlow(int n) {
init(n);
}
void init(int n) {
this->n = n;
e.clear();
g.assign(n, {});
cur.resize(n);
h.resize(n);
}
bool bfs(int s, int t) {
h.assign(n, -1);
std::queue<int> que;
h[s] = 0;
que.push(s);
while (!que.empty()) {
const int u = que.front();
que.pop();
for (int i : g[u]) {
auto [v, c] = e[i];
if (c > 0 && h[v] == -1) {
h[v] = h[u] + 1;
if (v == t) {
return true;
}
que.push(v);
}
}
}
return false;
}
T dfs(int u, int t, T f) {
if (u == t) {
return f;
}
auto r = f;
for (int &i = cur[u]; i < int(g[u].size()); ++i) {
const int j = g[u][i];
auto [v, c] = e[j];
if (c > 0 && h[v] == h[u] + 1) {
auto a = dfs(v, t, std::min(r, c));
e[j].cap -= a;
e[j ^ 1].cap += a;
r -= a;
if (r == 0) {
return f;
}
}
}
return f - r;
}
void addEdge(int u, int v, T c) {
g[u].push_back(e.size());
e.emplace_back(v, c);
g[v].push_back(e.size());
e.emplace_back(u, 0);
}
T flow(int s, int t) {
T ans = 0;
while (bfs(s, t)) {
cur.assign(n, 0);
ans += dfs(s, t, std::numeric_limits<T>::max());
}
return ans;
}
std::vector<bool> minCut() {
std::vector<bool> c(n);
for (int i = 0; i < n; i++) {
c[i] = (h[i] != -1);
}
return c;
}
struct Edge {
int from;
int to;
T cap;
T flow;
};
std::vector<Edge> edges() {
std::vector<Edge> a;
for (int i = 0; i < e.size(); i += 2) {
Edge x;
x.from = e[i + 1].to;
x.to = e[i].to;
x.cap = e[i].cap + e[i + 1].cap;
x.flow = e[i + 1].cap;
a.push_back(x);
}
return a;
}
};
const int N = 2e5 + 5;
std::vector<int> primes;
bool st[N];
void init(int n) {
for (int i = 2; i <= n; ++ i) {
if (!st[i]) {
primes.push_back(i);
}
for (auto & p : primes) {
if (p * i > n) {
break;
}
st[p * i] = true;
if (i % p == 0) {
break;
}
}
}
}
void solve() {
init(2e5);
int n, k;
std::cin >> n >> k;
std::vector<std::vector<std::array<int, 3>>> a(N);
for (int i = 0; i < n; ++ i) {
int p, c, l;
std::cin >> p >> c >> l;
a[l].push_back({p, c, i});
}
i64 flow = 0, sum = 0, one_id = n + 2, one_w = 0;;
MaxFlow<i64> f(n + 3);
int s = n, t = n + 1;
std::vector<std::array<int, 3>> b;
for (int i = 1; i < N; ++ i) {
for (auto & [px, cx, x] : a[i]) {
sum += px;
if (cx == 1) {
sum -= px;
if (px > one_w) {
sum -= one_w;
sum += px;
f.addEdge(s, one_id, px - one_w);
one_w = px;
}
} else if (cx & 1) {
f.addEdge(s, x, px);
} else {
f.addEdge(x, t, px);
}
for (auto & [py, cy, y] : b) {
if (!st[cx + cy] && cx != cy) {
if (cx & 1) {
if (cx == 1) {
f.addEdge(one_id, y, inf);
} else {
f.addEdge(x, y, inf);
}
} else {
if (cy == 1) {
f.addEdge(one_id, x, inf);
} else {
f.addEdge(y, x, inf);
}
}
}
}
b.push_back({px, cx, x});
}
flow += f.flow(s, t);
if (sum - flow >= k) {
std::cout << i << "\n";
return;
}
}
std::cout << -1 << "\n";
}
G. Anthem of Berland
题意:给定\(s, t\)两个字符串,\(s\)中有些地方没填,求\(t\)最多出现在\(s\)中多少次。
记\(n, m\)分别为\(s, t\)的长度。
记\(f[i][j]\)表示前\(i\)个字符匹配到了第\(j\)个位置的最大次数,那么就是用\(next\)数组进行转移,如果下一个可以匹配到\(m\)次数加一,然后跳到\(next[m]\)。
不过一直跳时间复杂度无法接受,用\(kmp\)自动机记录每个位置后面添加一个字符可以跳到哪里,就可以\(O(1)\)跳。
点击查看代码
void solve() {
std::string s, t;
std::cin >> s >> t;
int n = s.size(), m = t.size();
std::vector<int> next(m + 1);
for (int i = 2, j = 0; i <= m; ++ i) {
while (j && t[j] != t[i - 1]) {
j = next[j];
}
j += t[j] == t[i - 1];
next[i] = j;
}
std::vector aut(m, std::array<int, 26>{});
for (int i = 0; i < m; ++ i) {
for (int c = 0; c < 26; ++ c) {
if (c == t[i] - 'a') {
aut[i][c] = i + 1;
} else {
aut[i][c] = aut[next[i]][c];
}
}
}
const int inf = 1e9;
std::vector<int> f(m, -inf);
f[0] = 0;
for (int i = 0; i < n; ++ i) {
std::vector<int> g(m, -inf);
if (s[i] == '?') {
for (int j = 0; j < m; ++ j) {
if (f[j] < 0) {
continue;
}
for (int c = 0; c < 26; ++ c) {
int nj = aut[j][c], w = 0;
if (nj == m) {
nj = next[m];
w = 1;
}
g[nj] = std::max(g[nj], f[j] + w);
}
}
} else {
int c = s[i] - 'a';
for (int j = 0; j < m; ++ j) {
if (f[j] < 0) {
continue;
}
int nj = aut[j][c], w = 0;
if (nj == m) {
nj = next[m];
w = 1;
}
g[nj] = std::max(g[nj], f[j] + w);
}
}
f = g;
}
int ans = *std::max_element(f.begin(), f.end());
std::cout << ans << "\n";
}

浙公网安备 33010602011771号