VP Educational Codeforces Round 40 (Rated for Div. 2)
A. Diagonal Walking
模拟
点击查看代码
void solve() {
int n;
std::cin >> n;
std::string s;
std::cin >> s;
int ans = n;
for (int i = 0; i + 1 < n; ++ i) {
if ((s[i] == 'R' && s[i + 1] == 'U') || (s[i] == 'U' && s[i + 1] == 'R')) {
-- ans;
++ i;
}
}
std::cout << ans << "\n";
}
B. String Typing
题意:你要构造一个字符串,每次可以添加一个字符,你有一次操作机会可以把当前字符串复制一份到后面。求构造出给定字符串的最小次数。
找最长的前缀使得后面和它相邻的长度相等的串和它一样。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::string s;
std::cin >> s;
int ans = n;
for (int i = 0; i < n / 2; ++ i) {
if (s.substr(0, i + 1) == s.substr(i + 1, i + 1)) {
ans = std::min(ans, i + 1 + 1 + n - (i + 1) * 2);
}
}
std::cout << ans << "\n";
}
C. Matrix Walk
题意:一个矩阵\((i, j)\)位置上的数是\(y(i-1) + j\)。现在给出一个移动序列上每次到达位置上的数,只能上下左右移动。求\(x, y\)。
左右移动只会使得值减少一,上下移动则会减少\(y\)。那么我们记录相邻两个数的差,看除了1之外出现的数是不是只有一个。然后如果只有一个,就以这个数作为\(y\)。然后检查距离为1的数是不是在同一行。
\(x\)取个最大值就行。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
std::set<int> s;
for (int i = 0; i + 1 < n; ++ i) {
if (std::abs(a[i + 1] - a[i]) != 1) {
s.insert(std::abs(a[i + 1] - a[i]));
}
}
if (s.size() > 1) {
std::cout << "NO\n";
} else {
int x = 1e9;
if (s.empty()) {
std::cout << "YES\n";
std::cout << x << " " << 1 << "\n";
return;
}
int y = *s.begin();
if (y == 0) {
std::cout << "NO\n";
return;
}
for (int i = 0; i + 1 < n; ++ i) {
if (std::abs(a[i + 1] - a[i]) == 1 && (a[i] - 1) / y != (a[i + 1] - 1) / y) {
std::cout << "NO\n";
return;
}
}
std::cout << "YES\n";
std::cout << x << " " << y << "\n";
}
}
D. Fight Against Traffic
题意:给你一个图,有多少点使得向他们连边不会影响\(s\)到\(t\)的最短路?
\(s\)和\(t\)分别做一次\(dijkstra\)。
然后枚举点对,如果起点分别到两个点然后到终点的距离大于等于最短路,就合法。
点击查看代码
void solve() {
int n, m, s, t;
std::cin >> n >> m >> s >> t;
-- s, -- t;
std::vector<std::vector<int>> adj(n);
for (int i = 0; i < m; ++ i) {
int u, v;
std::cin >> u >> v;
-- u, -- v;
adj[u].push_back(v);
adj[v].push_back(u);
}
auto dijkstra = [&](std::vector<int> & dist, int s) -> void {
using PII = std::pair<int, int>;
std::priority_queue<PII, std::vector<PII>, std::greater<PII>> heap;
dist[s] = 0;
heap.emplace(dist[s], s);
while (heap.size()) {
auto [d, u] = heap.top(); heap.pop();
if (d != dist[u]) {
continue;
}
for (auto & v : adj[u]) {
if (dist[v] > d + 1) {
dist[v] = d + 1;
heap.emplace(dist[v], v);
}
}
}
};
const int inf = 1e9;
std::vector<int> dists(n, inf), distt(n, inf);
dijkstra(dists, s);
dijkstra(distt, t);
int ans = 0, d = dists[t];
for (int i = 0; i < n; ++ i) {
for (int j = i + 1; j < n; ++ j) {
if (dists[i] + 1 + distt[j] >= d && dists[j] + 1 + distt[i] >= d) {
++ ans;
}
}
}
std::cout << ans - m << "\n";
}
E. Water Taps
题意:\(x_i \in [0, a_i]\),要使得\(\sum_{i=1}^{n} x_i \times (t_i - T) = 0\),并且要求\(\sum_{i=1}^{n} x_i\)最大。\(x_i\)可以是实数。
直接贪心都选了,然后如果总和小于0,就把\(t_i -T\)为负数的从小到大去掉,如果大于0,则把\(t_i - T\)为正数的从大到小去掉。因为去掉的数可以是实数,所以这样一定更优。
点击查看代码
void solve() {
int n;
double T;
std::cin >> n >> T;
std::vector<std::pair<double, double>> a(n);
double ans = 0, sum1 = 0, sum2 = 0;
for (int i = 0; i < n; ++ i) {
std::cin >> a[i].second;
}
std::vector<std::pair<double, double>> b, c;
for (int i = 0; i < n; ++ i) {
std::cin >> a[i].first;
a[i].first -= T;
if (a[i].first == 0) {
ans += a[i].second;
} else if (a[i].first < 0) {
b.emplace_back(a[i]);
sum1 += a[i].first * a[i].second;
} else {
c.emplace_back(a[i]);
sum2 += a[i].first * a[i].second;
}
}
std::ranges::sort(b, std::greater<>());
std::ranges::sort(c);
if (sum1 == 0 || sum2 == 0) {
} else if (sum1 == sum2) {
for (auto & [y, x] : b) {
ans += x;
}
for (auto & [y, x] : c) {
ans += x;
}
} else if (-sum1 < sum2) {
double sum = sum1;
for (auto & [y, x] : b) {
ans += x;
}
for (auto & [y, x] : c) {
if (sum + x * y >= 0) {
ans += -sum / y;
break;
} else {
ans += x;
sum += x * y;
}
}
} else {
double sum = sum2;
for (auto & [y, x] : c) {
ans += x;
}
for (auto & [y, x] : b) {
if (sum + x * y <= 0) {
ans += -sum / y;
break;
} else {
ans += x;
sum += x * y;
}
}
}
std::cout << std::fixed << std::setprecision(12);
std::cout << ans << "\n";
}
F. Runner's Problem
题意:一个\(3 \times m\)的矩阵,你从\((1, 1)\)出发,每次可以走到下一列的行的距离不过1的位置,有\(n\)个障碍物,求到\((1, m)\)的路径方案数。
\(m\)十分大,这种一般是推公式,找规律,以及快速幂。
这题我们只能用快速幂,因为显然可以\(dp\),而我们常常用快速幂加速\(dp\)运算。
首先\(dp\)方程为\(f[i][j] = \sum f[i - 1][k](|k - j| \leq 1)\)。因为只有三行,那么我们可以手动构造符合条件的矩阵:
那么如果没有障碍物,直接就是\([0, 1, 0]\)乘上这个矩阵的\(m-1\)次方就行了。
现在考虑障碍物,发现\(n\)比较小,那么我们可以离散化,用障碍物把矩阵切成多段,分别计算即可。
代码省略取模类
点击查看代码
const int N = 3;
using Mat = std::array<std::array<Z, N>, N>;
struct Matrix {
Mat a;
Matrix() {
init();
}
void init() {
for (int i = 0; i < N; ++ i) {
for (int j = 0; j < N; ++ j) {
a[i][j] = 0;
}
}
}
std::array<Z, N> & operator[](int i) {
return a[i];
}
};
Matrix operator * (Matrix & a, Matrix & b) {
Matrix res;
for (int i = 0; i < N; ++ i) {
for (int j = 0; j < N; ++ j) {
for (int k = 0; k < N; ++ k) {
res[i][j] += a[i][k] * b[k][j];
}
}
}
return res;
}
void solve() {
i64 n, m;
std::cin >> n >> m;
std::vector<std::vector<std::pair<i64, i64>>> a(3);
std::vector<i64> b;
for (int i = 0; i < n; ++ i) {
int c;
i64 l, r;
std::cin >> c >> l >> r;
-- c;
a[c].emplace_back(l, r);
b.push_back(l);
b.push_back(r);
if (l - 1 >= 1) {
b.push_back(l - 1);
}
if (r + 1 <= m) {
b.push_back(r + 1);
}
}
b.push_back(1);
b.push_back(m);
std::sort(b.begin(), b.end());
b.erase(std::unique(b.begin(), b.end()), b.end());
auto get = [&](i64 x) -> i64 {
return std::ranges::lower_bound(b, x) - b.begin();
};
int k = b.size();
std::vector d(3, std::vector<int>(k + 1));
for (int i = 0; i < 3; ++ i) {
for (auto & [l, r] : a[i]) {
l = get(l), r = get(r);
d[i][l] += 1;
d[i][r + 1] -= 1;
}
for (int j = 1; j <= k; ++ j) {
d[i][j] += d[i][j - 1];
}
}
auto power = [&](Matrix a, i64 b) -> Matrix {
Matrix res;
res[0][0] = res[1][1] = res[2][2] = 1;
for (; b ; b >>= 1, a = a * a) {
if (b & 1) {
res = res * a;
}
}
return res;
};
Matrix ans;
ans[0][1] = 1;
for (int i = 1; i < k; ++ i) {
Matrix mat;
mat[0][0] = mat[1][0] = 1;
mat[0][1] = mat[1][1] = mat[2][1] = 1;
mat[1][2] = mat[2][2] = 1;
for (int j = 0; j < 3; ++ j) {
if (d[j][i]) {
mat[0][j] = mat[1][j] = mat[2][j] = 0;
}
}
mat = power(mat, b[i] - b[i - 1]);
ans = ans * mat;
}
std::cout << ans[0][1] << "\n";
}
G. Castle Defense
题意:给你一个数组\(a\),\(b_i\)的值是\(suma[i - r ..., i + r]\)。你可以进行\(k\)次操作,每次把一个\(a_i\)加一,你要使得最小\(b_i\)最大。
最小最大,触发关键词,考虑二分。
我们可以把小于\(mid\)的位置的\(i\)和\(mid - b_i\)都拿出来,然后我们考虑维护一个差分数组,从小到大看,如果当前数加上\(d_i\)依然小于\(mid\),那么我们只能给它加数,加到哪里是最优的?显然我们应该顾及到尽量后面的位置,所以应该建在\(i + r\)这个位置,那么可以影响到\(i + 2 \times r\),那么可以差分维护这个区间加操作。
点击查看代码
void solve() {
int n, R;
i64 k;
std::cin >> n >> R >> k;
std::vector<i64> 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];
}
auto check = [&](i64 x) -> bool {
std::vector<std::pair<int, i64>> b{{0, 0}};
for (int i = 1; i <= n; ++ i) {
i64 v = sum[std::min(n, i + R)] - sum[std::max(0, i - R - 1)];
if (v < x) {
if (x - v > k) {
return false;
}
b.emplace_back(i, x - v);
}
}
i64 tot = 0;
int m = b.size();
std::vector<i64> d(m + 2);
for (int i = 1, j = 1; i < m; ++ i) {
while (j < m && b[j].first - b[i].first <= 2 * R) {
++ j;
}
d[i] += d[i - 1];
if (d[i] < b[i].second) {
d[j] -= b[i].second - d[i];
tot += b[i].second - d[i];
d[i] = b[i].second;
}
if (tot > k) {
return false;
}
}
return true;
};
i64 l = 0, r = 2e18;
while (l < r) {
i64 mid = l + r + 1 >> 1ll;
if (check(mid)) {
l = mid;
} else {
r = mid - 1;
}
}
std::cout << l << "\n";
}

浙公网安备 33010602011771号