VP Educational Codeforces Round 22
A. The Contest
题意:有\(n\)个数的和\(sum\),在\(m\)的个区间里如果\(l_i \leq sum \leq r_i\)则输出\(sum\),否则如果\(l_i \geq sum\),输出\(l_i\)。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
int m;
std::cin >> m;
std::vector<int> l(m), r(m);
for (int i = 0; i < m; ++ i) {
std::cin >> l[i] >> r[i];
}
i64 sum = std::accumulate(a.begin(), a.end(), 0ll);
for (int i = 0; i < m; ++ i) {
if (l[i] >= sum) {
std::cout << l[i] << "\n";
return;
} else if (l[i] <= sum && r[i] >= sum) {
std::cout << sum << "\n";
return;
}
}
std::cout << -1 << "\n";
}
B. The Golden Age
题意:给你一个\(x, y\),求所有\(x^a + y^b\)在\([l, r]\)里构成的最大间隙。
因为是幂,则数量很少,可以直接求出所有在\([l, r]\)数。然后排序后枚举两个相邻的数取最大差就行。
点击查看代码
void solve() {
i64 x, y, l, r;
std::cin >> x >> y >> l >> r;
std::vector<i64> a, b;
for (i128 i = 1; i <= r; i *= x) {
a.push_back(i);
}
for (i128 i = 1; i <= r; i *= y) {
b.push_back(i);
}
std::vector<i64> c;
for (auto & i : a) {
for (auto & j : b) {
if (i + j >= l && i + j <= r) {
c.push_back(i + j);
}
}
}
c.push_back(l - 1);
c.push_back(r + 1);
std::sort(c.begin(), c.end());
int n = c.size();
i64 ans = 0;
for (int i = 1; i < n; ++ i) {
// std::cout << c[i] << " \n"[i == n - 1];
ans = std::max(ans, c[i] - c[i - 1] - 1);
}
std::cout << ans << "\n";
}
C. The Tag Game
题意:在一棵树上,\(Alcie\)在\(1\)点,\(Bob\)在\(x\)点,两个人轮流移动,\(Bob\)先动,可以停留在当前点上,求\(Alice\)几回合后可以捉到\(Bob\)。
以这两个点为根做两次\(bfs\),求出每个点到这两个点的距离。如果这个这个点\(Bob\)可以比\(Alice\)先到达,那么就可以取\(Alice\)到这个点的最大值,因为\(Bob\)到这个点后假设一直不动,就需要\(Alice\)跑过来。而最优情况肯定是\(Bob\)一直跑,直到一个不能跑了就一直停留。
点击查看代码
void solve() {
int n, x;
std::cin >> n >> x;
-- x;
std::vector<std::vector<int>> adj(n);
for (int i = 1; i < n; ++ i) {
int u, v;
std::cin >> u >> v;
-- u, -- v;
adj[u].push_back(v);
adj[v].push_back(u);
}
std::vector d(2, std::vector<int>(n, -1));
auto work = [&](int s, int i) -> void {
std::queue<int> q;
q.push(s);
d[i][s] = 0;
while (q.size()) {
int u = q.front(); q.pop();
for (auto & v : adj[u]) {
if (d[i][v] == -1) {
d[i][v] = d[i][u] + 1;
q.push(v);
}
}
}
};
work(0, 0); work(x, 1);
int ans = 0;
for (int i = 0; i < n; ++ i) {
if (d[1][i] < d[0][i]) {
ans = std::max(ans, d[0][i]);
}
}
std::cout << ans * 2 << "\n";
}
D. Two Melodies
题意:在一个数组种选两个不相交的子序列,每个子序列相邻的两个数要么相差1,要么余7相同。使得两个子序列长度加起来最大。
如果我们向每个\(|a_i - a_j| = 1\)或\(a_i \equiv a_j \pmod{7}\)的\((i, j)\)由\(i\)向\(j\)连边。那么就等价于求图里的两段路径,使得路径长度之和最大,这是个经典费用流问题。我们把每个点拆成出度和入度,一个点的入度向出度连费用为1流量为1的边,可以连边的由出度向入度连费用为0流量为1的边,同时源点给每个点的入度连费用为0流量为1的边,每个点的出度向汇点连费用为0流量为1的边,然后为了满足两条路径的需求,超级源点向源点连费用为0流量为2的边,汇点向超级汇点连费用为0流量为2的边。但这样边数太大,时间复杂度不能接受。
我们可以对于每个\(i\)的出度,往右边最近的\(a_i - a_j = 1\)以及\(a_i - a_j = -1\)的\(j\)的入度连边,因为满足条件的\(a_j\)值只有两种,我们选最近的可以给后面留出更大空间,显然更优。然后对于\(a_i \equiv a_j \pmod{7}\)的最近的\(j\),我们由\(i\)的出度向\(j\)的入度连边,同时\(i\)的入度向\(j\)的入度连边,这意味着选了\(i\)可以继续选\(j\),或者如果不选\(i\)下一个就可以选\(j\)。这样就解决了。
这个问题可以由\(2\)条路径扩展成\(k\)条路径,只需要该超级源点和超级汇点与源点汇点之间的流量。
点击查看代码
template<class T>
struct MinCostFlow {
struct _Edge {
int to;
T cap;
T cost;
_Edge(int to_, T cap_, T cost_) : to(to_), cap(cap_), cost(cost_) {}
};
int n;
std::vector<_Edge> e;
std::vector<std::vector<int>> g;
std::vector<T> h, dis;
std::vector<int> pre;
bool dijkstra(int s, int t) {
dis.assign(n, std::numeric_limits<T>::max());
pre.assign(n, -1);
std::priority_queue<std::pair<T, int>, std::vector<std::pair<T, int>>, std::greater<std::pair<T, int>>> que;
dis[s] = 0;
que.emplace(0, s);
while (!que.empty()) {
T d = que.top().first;
int u = que.top().second;
que.pop();
if (dis[u] != d) {
continue;
}
for (int i : g[u]) {
int v = e[i].to;
T cap = e[i].cap;
T cost = e[i].cost;
if (cap > 0 && dis[v] > d + h[u] - h[v] + cost) {
dis[v] = d + h[u] - h[v] + cost;
pre[v] = i;
que.emplace(dis[v], v);
}
}
}
return dis[t] != std::numeric_limits<T>::max();
}
MinCostFlow() {}
MinCostFlow(int n_) {
init(n_);
}
void init(int n_) {
n = n_;
e.clear();
g.assign(n, {});
}
void addEdge(int u, int v, T cap, T cost) {
g[u].push_back(e.size());
e.emplace_back(v, cap, cost);
g[v].push_back(e.size());
e.emplace_back(u, 0, -cost);
}
std::pair<T, T> flow(int s, int t) {
T flow = 0;
T cost = 0;
h.assign(n, 0);
while (dijkstra(s, t)) {
for (int i = 0; i < n; ++i) {
h[i] += dis[i];
}
T aug = std::numeric_limits<int>::max();
for (int i = t; i != s; i = e[pre[i] ^ 1].to) {
aug = std::min(aug, e[pre[i]].cap);
}
for (int i = t; i != s; i = e[pre[i] ^ 1].to) {
e[pre[i]].cap -= aug;
e[pre[i] ^ 1].cap += aug;
}
flow += aug;
cost += aug * h[t];
}
return std::make_pair(flow, cost);
}
struct Edge {
int from;
int to;
T cap;
T cost;
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.cost = e[i].cost;
x.flow = e[i + 1].cap;
a.push_back(x);
}
return a;
}
};
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
MinCostFlow<int> mf(2 * n + 4);
int s = 2 * n, t = 2 * n + 1, S = 2 * n + 2, T = 2 * n + 3;
mf.addEdge(S, s, 2, 0);
mf.addEdge(t, T, 2, 0);
for (int i = 0; i < n; ++ i) {
mf.addEdge(s, i, 1, 0);
mf.addEdge(i + n, t, 1, 0);
mf.addEdge(i, i + n, 1, -1);
for (int j = i + 1; j < n; ++ j) {
if (a[i] == a[j] + 1) {
mf.addEdge(i + n, j, 1, 0);
break;
}
}
for (int j = i + 1; j < n; ++ j) {
if (a[i] == a[j] - 1) {
mf.addEdge(i + n, j, 1, 0);
break;
}
}
for (int j = i + 1; j < n; ++ j) {
if (a[i] % 7 == a[j] % 7) {
mf.addEdge(i + n, j, 1, 0);
mf.addEdge(i, j, 1, 0);
break;
}
}
}
std::cout << -mf.flow(S, T).second << "\n";
}
E. Army Creation
题意:有\(n\)个数,每个类型的数最多拿\(k\)个,每次求\([l, r]\)这个区间里可以选几个数。
记\(i\)的左边的第\(k\)个相同数的位置\(p_i\),没有则\(p_i = 0\)。那么对于\([l, r]\)的数,我们先全部选上,然后看要删掉哪些数,对于\(p_i \geq l_i\)的数就不能选。则我们用主席树维护这个\(p_i\)的区间和,看\([l, r]\)这个区间有多少数的\(p_i\)在\([l, r]\)之间。
点击查看代码
#define ls(u) tr[u].lson
#define rs(u) tr[u].rson
const int N = 1e5 + 5;
struct Node {
int lson, rson;
int sum;
}tr[N << 5];
int tot = 0;
int root[N];
void insert(int & u, int v, int l, int r, int p) {
u = ++ tot;
tr[u] = tr[v];
tr[u].sum += 1;
if (l == r) {
return;
}
int mid = l + r >> 1;
if (p <= mid) {
insert(ls(u), ls(v), l, mid, p);
} else {
insert(rs(u), rs(v), mid + 1, r, p);
}
}
int query(int u, int v, int l, int r, int L, int R) {
if (!u) {
return 0;
}
if (L <= l && r <= R) {
return tr[u].sum - tr[v].sum;
}
int mid = l + r >> 1;
if (R <= mid) {
return query(ls(u), ls(v), l, mid, L, R);
} else if (L > mid) {
return query(rs(u), rs(v), mid + 1, r, L, R);
}
return query(ls(u), ls(v), l, mid, L, mid) + query(rs(u), rs(v), mid + 1, r, mid + 1, R);
}
void solve() {
int n, k;
std::cin >> n >> k;
std::vector<std::vector<int>> pos(N);
std::vector<int> a(n + 1);
for (int i = 1; i <= n; ++ i) {
std::cin >> a[i];
pos[a[i]].push_back(i);
int p = 0;
if (pos[a[i]].size() > k) {
p = pos[a[i]][(int)pos[a[i]].size() - 1 - k];
}
insert(root[i], root[i - 1], 0, n, p);
}
int q;
std::cin >> q;
int last = 0;
while (q -- ) {
int l, r;
std::cin >> l >> r;
l = (l + last) % n + 1;
r = (r + last) % n + 1;
if (l > r) {
std::swap(l, r);
}
last = r - l + 1 - query(root[r], root[l - 1], 0, n, l, r);
std::cout << last << "\n";
}
}