VP Educational Codeforces Round 58 (Rated for Div. 2)


A. Minimum Integer

题意:找最小的小于\(l\)或者大于\(r\)\(d\)的倍数。

点击查看代码
void solve() {
	int l, r, d;
	std::cin >> l >> r >> d;
	if (l > d) {
		std::cout << d << "\n";
	} else {
		std::cout << (r / d + 1) * d << "\n";
	}
}

B. Accordion

题意:合法字符串为"\([:\)" + 若干个"\(|\)" + "\(:]\)"。你可以删除当前字符串的一些字符,可以得到的最长合法字符串的长度是多少?

找出最左边的"\([:\)和最右边的"\(:]\)"。

点击查看代码
void solve() {
    std::string s;
    std::cin >> s;
    int n = s.size();
    int l = 0, j = 0;
    while (l < n && j < 2) {
    	if (j == 0 && s[l] == '[') {
    		++ j;
    	} else if (j == 1 && s[l] == ':') {
    		++ j;
    	}
    	++ l;
    }

    int r = n - 1;
    j = 0;
    while (r >= 0 && j < 2) {
    	if (j == 0 && s[r] == ']') {
    		++ j;
    	} else if (j == 1 && s[r] == ':') {
    		++ j;
    	}
    	-- r;
    }


    if (l > r + 1) {
    	std::cout << -1 << "\n";
    	return;
    }

    int ans = 4;
    for (int i = l; i <= r; ++ i) {
    	ans += s[i] == '|';
    }
    std::cout << ans << "\n";
}

C. Division and Union

题意:\(n\)个区间,分成两个非空组。使得每组任意一个区间和另一个的任意一个区间都不相交。

双指针典题。实际是求区间合并后的区间数是不是大于1。
你们可以按左端点排序,然后维护一个右端点的最大值。每次遇到一个新点,如果它的左端点小于等于最大值,那么它和之前区间有交。否则没交,我们就可以分成两个组了。

点击查看代码
void solve() {
    int n;
    std::cin >> n;
    std::vector<std::array<int, 3>> a(n);
    for (int i = 0; i < n; ++ i) {
    	int l, r;
    	std::cin >> l >> r;
    	a[i] = {l, r, i};
    }

    std::ranges::sort(a);

    std::vector<int> ans(n);
    for (int i = 0, j = 0, max = a[i][1]; i < n; ++ i) {
    	if (a[i][0] > max) {
    		break;
    	}

    	while (j < n && a[j][0] <= max) {
    		max = std::max(max, a[j][1]);
    		ans[a[j][2]] = 1;
    		++ j;
    	}

    	if (j == n) {
    		std::cout << -1 << "\n";
    		return;
    	}
    }

    for (int i = 0; i < n; ++ i) {
    	std::cout << ans[i] + 1 << " \n"[i == n - 1];
    }
}

D. GCD Counting

题意:给你一棵树,记\(g(x, y)\)\(x\)\(y\)的路径的\(gcd\),求所有\(g(x, y) > 1\)\(x, y\)的最大\(dist(x, y)\)

考虑树形\(dp\)。将每个数进行质因子分解。然后\(f[i][j]\)记为以\(i\)为根的子树包含\(i\)且最大公约数是\(a_i\)的第\(j\)个质因子的倍数的路径的最大长度。那么就类似于求树的直径,我们可以枚举所有\(u, v\)相同的质因子,\(ans = \max(ans, f[u][i] + f[v][j])\)\(f[u][i] = \max(f[u][i], f[v][j] + 1)\)。其中\(ans\)是路径最大值。

点击查看代码
void solve() {
    int n;
    std::cin >> n;
    std::vector<int> a(n);
    for (int i = 0; i < n; ++ i) {
    	std::cin >> a[i];
    }

    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);
    }

    if (std::ranges::count(a, 1) == n) {
    	std::cout << 0 << "\n";
    	return;
    }

    const int N = 2e5 + 5;
    std::vector<std::vector<int>> g(N);
    for (int i = 2; i < N; ++ i) {
    	int x = i;
    	for (int j = 2; j <= x / j; ++ j) {
    		if (x % j == 0) {
    			g[i].push_back(j);
    			while (x % j == 0) {
    				x /= j;
    			}
    		}
    	}

    	if (x > 1) {
    		g[i].push_back(x);
    	}
    }

    std::vector f(n, std::vector<int>(10, 1));
    int ans = 1;
    auto dfs = [&](auto & self, int u, int fa) -> void {
    	for (auto & v : adj[u]) {
    		if (v == fa) {
    			continue;
    		}

    		self(self, v, u);
    		for (int i = 0; i < g[a[u]].size(); ++ i) {
    			for (int j = 0; j < g[a[v]].size(); ++ j) {
    				if (g[a[u]][i] == g[a[v]][j]) {
    					ans = std::max(ans, f[u][i] + f[v][j]);
    					f[u][i] = std::max(f[u][i], f[v][j] + 1);
    				}
    			}
    		}
    	}
    };

    dfs(dfs, 0, -1);
    std::cout << ans << "\n";
}

E. Polycarp's New Job

题意:两种操作,一种是把\((x, y)\)加入集合,一种是求集合里每个数是不是都有\(x \leq\ h\ and\ y \leq w\)\(x \leq\ w\ and\ y \leq\ h\)

对于\(h \geq w\),且\(x \geq y\),如果\((y, x)\)可以满足,则\((x, y)\)也能满足,所以我们让第一关键字大于等于第二关键字存下来就行。

点击查看代码
void solve() {
    int n;
    std::cin >> n;
    int x = 0, y = 0;
    for (int i = 0; i < n; ++ i) {
    	std::string op;
    	std::cin >> op;
    	if (op == "+") {
    		int a, b;
    		std::cin >> a >> b;
    		if (a < b) {
    			std::swap(a, b);
    		}

    		x = std::max(x, a), y = std::max(y, b);
    	} else {
    		int h, w;
    		std::cin >> h >> w;
    		if (h < w) {
    			std::swap(h, w);
    		}

    		if (x <= h && y <= w) {
    			std::cout << "YES\n";
    		} else {
    			std::cout << "NO\n";
    		}
    	}
    }
}


F. Trucks and Cities

题意:数轴上有\(n\)个点,有\(m\)辆车,第\(i\)辆车从\(s_i\)\(t_i\),一格花费\(c_i\)升油,中途可以最多在\(r_i\)个点加油。假设油箱容量为\(V\),则加满油后容量变成\(V\),一开始油箱是满的。每辆车的油箱容量是一样的,求最小的\(V\)使得所有车可以走完。

观察到\(n\)比较小,可以使用区间\(dp\)\(f[i][j][k]\)表示从\(i\)\(j\)加油\(k\)次的路径段的最大段最小的长度。你们一辆花费为\(c_i\)的车油箱容量为\(f[i][j][k] \times c_i\)
转移就是枚举前\(k-1\)个最后加油的地点,\(f[i][j][k] = \min(f[i][l][k - 1], a[j] - a[l])\)
只不过这个复杂度是\(n^4\)。无法通过。考虑优化,发现\(l\)\(i+1\)开始,随着\(l\)增大,\(f[i][l][k-1]\)递增,\(a[j] - a[l]\)递减。因为是求两者最大值。所以答案一定在最后一个\(f[i][l][k-1] < a[j] - a[l]\)上,用个指针维护就行。

点击查看代码
void solve() {
	int n, m;
	std::cin >> n >> m;
	std::vector<int> a(n);
	for (int i = 0; i < n; ++ i) {
		std::cin >> a[i];
	}

	std::vector<std::vector<std::array<int, 3>>> Q(n);
	for (int i = 0; i < m; ++ i) {
		int s, t, c, r;
		std::cin >> s >> t >> c >> r;
		-- s, -- t;
		Q[std::min(r, t - s)].push_back(std::array<int, 3>{s, t, c});
	}

	const int inf = 1e9; 
	std::vector f(n, std::vector<int>(n, inf));
	for (int i = 0; i < n; ++ i) {
		for (int j = i; j < n; ++ j) {
			f[i][j] = a[j] - a[i];
		}
	}

	i64 ans = 0;
	for (int k = 0; k < n; ++ k) {
		for (auto & [s, t, c] : Q[k]) {
			ans = std::max(ans, (i64)f[s][t] * c);
		}

		auto g = f;
		for (int i = 0, l = i; i < n; ++ i) {
			for (int j = i + 1; j < n; ++ j) {
				while (f[i][l] < a[j] - a[l]) {
					++ l;
				}

				g[i][j] = std::min({g[i][j], f[i][l], a[j] - a[l - 1]});
			}
		}

		f = g;
	}

	std::cout << ans << "\n";
}
posted @ 2025-04-14 16:18  maburb  阅读(15)  评论(0)    收藏  举报