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)\)。因为只有三行,那么我们可以手动构造符合条件的矩阵:

\[\begin{bmatrix} 1&1&0\\ 1&1&1\\ 0&1&1\\ \end{bmatrix}\]

那么如果没有障碍物,直接就是\([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";
}
posted @ 2025-03-24 15:26  maburb  阅读(13)  评论(0)    收藏  举报