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


A. Inscribed Figures

题意:三种图形:圆、等腰三角形、正方形。给出它们的嵌套序列,里面的元素是可以放在外面元素里面的最大元素。求交点个数。

显然三角形和正方形嵌套会有无数个交点。圆和三角形嵌套不管谁在外面谁在里面都是三个交点,同理圆和正方形是四个交点。
还有个特殊情况,就是正方形、圆、三角形会有一个点被多算一次,减去这种情况多加的点就是正确答案了。

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

    int ans = 0;
    for (int i = 1; i < n; ++ i) {
    	if (a[i] != 1 && a[i - 1] != 1) {
    		std::cout << "Infinite\n";
    		return;
    	}

    	ans += std::max(a[i], a[i - 1]) + 1;
    	if (i >= 2 && a[i - 2] == 3 && a[i - 1] == 1 && a[i] == 2) {
    		-- ans;
    	}
    }

    std::cout << "Finite\n";
    std::cout << ans << "\n";
}

B. Ugly Pairs

题意:给你一个字符串,重排它使得没有两个相邻的字符字典序只相差1。

把字典序按奇偶分成两个字符串,相同字符都放一起就行。然后判断它们能不能凑出合法的字符串就行。

点击查看代码
void solve() {
	std::string s;
	std::cin >> s;
	std::array<int, 26> cnt{};
	for (auto & c : s) {
		cnt[c - 'a'] += 1;
	} 

	std::string ans1, ans2;
	for (int i = 0; i < 26; i += 2) {
		ans1 += std::string(cnt[i], 'a' + i);
	}

	for (int i = 1; i < 26; i += 2) {
		ans2 += std::string(cnt[i], 'a' + i);
	}

	if (ans1.empty()) {
		std::cout << ans2 << "\n";
	} else if (ans2.empty()) {
		std::cout << ans1 << "\n";
	} else if (std::abs(ans1.back() - ans2[0]) != 1) {
		std::cout << ans1 << ans2 << "\n";
	} else if (std::abs(ans2.back() - ans1[0]) != 1) {
		std::cout << ans2 << ans1 << "\n";
	} else {
		std::cout << "No answer\n";
	}
}

C. Match Points

题意:\(n\)个数,每次选两个数使得它们的差大于等于\(m\),每个数只能选一次,求最多选几对。

考虑二分。
如果需要凑\(mid\)对,那么肯定是最小的\(mid\)个和最大的\(mid\)个匹配,这样一定是最大可能匹配成功的。也就是判断\(i \in [1, mid], a_{n - mid + i} - a_i \geq m\)是否都成立。

点击查看代码
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::ranges::sort(a);

    auto check = [&](int k) -> bool {
    	for (int i = 0, j = n - k; i < k; ++ i, ++ j) {
    		if (a[j] - a[i] < m) {
    			return false;
    		}
    	}

    	return true;
    };

    int l = 0, r = n / 2;
    while (l < r) {
    	int mid = l + r + 1 >> 1;
    	if (check(mid)) {
    		l = mid;
    	} else {
    		r = mid - 1;
    	}
    }

    std::cout << l << "\n";
}

D. 0-1-Tree

题意:给你一棵树,每条边有边权,合法路径为边权的数字连接起来是前面若干个\(0\)加后面若干个\(1\)。求合法路径数。

树形\(dp\),记\(f[u][0/1/2/3]\)为从\(u\)往下全\(0\)、全\(1\)、先\(0\)\(1\)、先\(1\)\(0\)的路径数。那么对于子树\(v\),记录从\(u\)\(v\)的子树的路径为\(g\),那么\(ans += f[u][0] * g[0] * 2 + f[u][1] * g[1] * 2 + f[u][0] * g[1] + f[u][0] * g[2] + f[u][1] * g[0] + f[u][1] * g[3] + f[u][2] * g[0] + f[u][3] * g[1]\),意为讨论取\(f[u][i]\)这样的路径时\(v\)和它可以拼凑的合法路径。

点击查看代码
void solve() {
    int n;
    std::cin >> n;
    std::vector<std::vector<std::pair<int, int>>> adj(n);
    for (int i = 1; i < n; ++ i) {
    	int u, v, w;
    	std::cin >> u >> v >> w;
    	-- u, -- v;
    	adj[u].emplace_back(v, w);
    	adj[v].emplace_back(u, w);
    }

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

    		self(self, v, u);
    		std::array<i64, 4> g{};
    		if (w == 0) {
    			g[0] = f[v][0] + 1;
    			g[2] = f[v][2] + f[v][1];
    		} else {
    			g[1] = f[v][1] + 1;
    			g[3] = f[v][3] + f[v][0];
    		}

    		ans += f[u][0] * g[0] * 2 + f[u][1] * g[1] * 2;
    		ans += f[u][0] * g[1] + f[u][0] * g[2];
    		ans += f[u][1] * g[0] + f[u][1] * g[3];
    		ans += f[u][2] * g[0];
    		ans += f[u][3] * g[1];

    		f[u][0] += g[0];
    		f[u][1] += g[1];
    		f[u][2] += g[2];
    		f[u][3] += g[3];
    	}

    	ans += f[u][0] * 2 + f[u][1] * 2 + f[u][2] + f[u][3];
    };

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

E. Special Segments of Permutation

题意:一个排列,求有多少\(l, r\)满足\(p_l + p_r = \max_{i=l}^{r} p_i\)

用单调队列求出每个数最为最大值的区间\(L_i, R_i\)。然后用\(pos_i\)记录\(i\)出现的位置。
那么对于每个\(i\),我们可以枚举\([L_i, i - 1]\)\([i + 1, R_i]\)中较小的区间。看\(p_i - p_j\)是否在另一个区间里。
复杂度是\(O(nlogn)\),因为第\(i\)个数的区间长度一定小于\(i\),那么较小的区间长度小于等于\(\lfloor \frac{i}{2} \rfloor\),这个区间里的数都小于等于它,那么其中的次大数的贡献的区间小于等于\(\lfloor \frac{i}{4} \rfloor\),依次类推,其区间里的其它数贡献的区间加起来都没它多。

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

    std::vector<int> L(n + 1), R(n + 1);
    std::stack<int> stk;
    for (int i = 0; i < n; ++ i) {
    	while (stk.size() && a[stk.top()] < a[i]) {
    		R[stk.top()] = i - 1;
    		stk.pop();
    	}

    	stk.push(i);
    }

    while (stk.size()) {
    	R[stk.top()] = n - 1;
    	stk.pop();
    }

    for (int i = n - 1; i >= 0; -- i) {
    	while (stk.size() && a[stk.top()] < a[i]) {
    		L[stk.top()] = i + 1;
    		stk.pop();
    	}

    	stk.push(i);
    }

    while (stk.size()) {
    	L[stk.top()] = 0;
    	stk.pop();
    }

  	i64 ans = 0;
  	for (int i = 0; i < n; ++ i) {
  		if (i - L[i] < R[i] - i) {
  			for (int j = L[i]; j < i; ++ j) {
  				ans += pos[a[i] - a[j]] > i && pos[a[i] - a[j]] <= R[i];
  			}
  		} else {
  			for (int j = i + 1; j <= R[i]; ++ j) {
  				ans += pos[a[i] - a[j]] < i && pos[a[i] - a[j]] >= L[i];
  			}
  		}
  	}

  	std::cout << ans << "\n";
}

F. Card Bag

题意:\(n\)个数,每次随机选一个数出来,不放回。如果当前数比前面数小,则输,如果相等,则赢,如果大于则继续游戏。求赢的概率。

赢的场面是一个严格上升的序列然后加上和最后一个数相同的数。那么我们可以从小到大\(dp\)
\(cnt[i]\)为第\(i\)个数的数量,\(f[i][j]\)表示到第\(i\)个数总共抽了\(j\)张卡的概率。那么可得\(f[i][j] = \sum_{k=0}^{i-1} f[k][j - 1] \times \frac{cnt[i]}{n - j + 1}\)。意为前面所有选了\(j-1\)张卡的情况,乘上当前选一张\(i\)的概率。答案则可加上连续选两张\(i\)的概率:\(\sum_{k=0}^{i-1} f[k][j - 1] \frac{cnt[i]}{n - j + 1} \times \frac{cnt[i] - 1}{n - j}\)
不难发现\(\sum_{k=0}^{i-1} f[k][j - 1]\)可以用前缀和维护。

点击查看代码
const int mod = 998244353;

void solve() {
    int n;
    std::cin >> n;
    std::vector<int> cnt(n + 1);
    for (int i = 0; i < n; ++ i) {
    	int x;
    	std::cin >> x;
    	++ cnt[x];
    }

    std::vector<int> inv(n + 1);
    inv[1] = 1;
    for (int i = 2; i <= n; ++ i) {
    	inv[i] = (i64)(mod - mod / i) * inv[mod % i] % mod;
    }

    int ans = 0;
    std::vector<int> f(n + 1), sum(n + 1);
    sum[0] = 1;
    for (int i = 1; i <= n; ++ i) {
    	std::vector<int> g(n + 1), nsum(n + 1);
    	nsum[0] = 1;
    	for (int j = 1; j <= i; ++ j) {
    		g[j] = (i64)sum[j - 1] * cnt[i] % mod * inv[n - j + 1] % mod;
    		ans = (ans + (i64)sum[j - 1] * cnt[i] % mod * inv[n - j + 1] % mod * (cnt[i] - 1) % mod * inv[n - j] % mod) % mod;
    		nsum[j] = (sum[j] + g[j]) % mod;
    	}

    	f = g;
    	sum = nsum;
    }

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