Atto Round 1 (Codeforces Round 1041, Div. 1 + Div. 2)


A. Mix Mex Max

题意:一个数组,有些地方没填。你需要给这些位置填上数使得数组里任意连续的三个数的\(mex = max - min\)

\(mex = 0\)时,\(max = min\);当\(mex \ne 0\)时,\(min = 0, max - min = max\)。而\(mex \ne max\)。所以只有\(mex = 0\)且三个数都相同才行。

点击查看代码
#include <bits/stdc++.h>

using i64 = long long;

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 < n; ++ i) {
		if (a[i] != -1) {
			s.insert(a[i]);
		}
	}

	if (s.size() > 1 || (s.size() == 1 && *s.begin() == 0)) {
		std::cout << "NO\n";
	} else {
		std::cout << "YES\n";
	}
}

int main() {
	std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
	int t = 1;
	std::cin >> t;
	while (t -- ) {
		solve();
	}
	return 0;
}

B. Hamiiid, Haaamid... Hamid?

题意:有\(n\)个点,有些地方是墙,有些地方是空地。你一开始在一个空地上坐标是\(x\),每回合另一个人会在除你所在的空地外的其它空地建一面墙,然后你可以选择向左或向右,走到你选择的方向距离你最近的墙的位置并且把这个墙摧毁。如果这个方向没有墙挡住你就胜利。求回合数。

方向是一开始选择后就不会变的。因为左右切换撞的墙一定比往一个方向走撞的多。
那么答案最差是\(\min(x, n - x + 1)\),然后如果一开始另一个人没有把某一边挨着\(x\)的位置变成墙,则可以直接跑到这一边最近的墙的位置,设左边最近墙的位置为\(l\),右边最近墙位置为\(r\)。如果一开始把\(x-1\)变成墙,那么往右走答案是\(n-r+2\),如果一开始把\(x+\)变成墙,答案是\(l+1\),另一个人肯定在这两个情况里选最大的,所以\(ans = \min(ans, \max(n - r + 2, l + 1))\)

点击查看代码
#include <bits/stdc++.h>

using i64 = long long;

void solve() {
	int n, x;
	std::cin >> n >> x;
	-- x;
	std::string s;
	std::cin >> s;
	int l = x, r = x;
	while (l >= 0 && s[l] == '.') {
		-- l;
	}

	while (r < n && s[r] == '.') {
		++ r;
	}

	int ans = std::min(x + 1, n - x);
	int max = std::max(n - r + 1, l + 2);

	ans = std::min(ans, max);

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

int main() {
	std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
	int t = 1;
	std::cin >> t;
	while (t -- ) {
		solve();
	}
	return 0;
}

C. Trip Shopping

题意:两个数组\(a, b\),你要进行\(n\)次操作,每次选择两个位置\(i, j\),然后另一个人随意交换\(a_i, a_j, b_i, b_j\)这四个数。最终答案为\(\sum_{i=1}^{n} |a_i - a_j|\)。你需要答案尽可能小,另一个希望答案尽可能大,问最终答案。

最优操作是始终只交换一对\(i, j\)。也就是应该选\(i, j\)操作\(k\)次。因为另一个人肯定是拿最大数减最小数,次大数减次小数贡献最大,所以我们选择大于\(1\)对位置只会使得答案变大。
那么如果有\((a_i, b_i)\)\((a_j, b_j)\)满足\(a_i \leq a_j \leq b_j \leq b_i\)\(a_i \leq a_j \leq b_i \leq b_j\),则它们已经达到了最大值,另一个不管怎么交换也不会使得答案变大了,所以我们我们按\(a_i\)排序,记录前缀最大的\(b_i\),如果\(max_{b} \geq a_i\)则找到了这样的一对,直接输出答案。
否则任意一对都没有交集,也就是\(a_i \leq b_i \leq a_j \leq b_j\)。此时选择\(a_j - b_i\)最小的,答案增加\((a_j - b_i) \times 2\)

点击查看代码
#include <bits/stdc++.h>

using i64 = long long;

void solve() {
	int n, k;
	std::cin >> n >> k;
	std::vector<i64> a(n), b(n);
	for (int i = 0; i < n; ++ i) {
		std::cin >> a[i];
	}

	for (int i = 0; i < n; ++ i) {
		std::cin >> b[i];
	}

	i64 ans = 0;
	for (int i = 0; i < n; ++ i) {
		ans += std::abs(a[i] - b[i]);
	}

	std::vector<std::pair<i64, i64>> c(n);
	for (int i = 0; i < n; ++ i) {
		c[i] = {std::min(a[i], b[i]), std::max(a[i], b[i])};
	}

	std::ranges::sort(c, [&](std::pair<i64, i64> & a, std::pair<i64, i64> & b) {
		if (a.first == b.first) {
			return a.second > b.second;
		}
		return a.first < b.first;
	});

	i64 min = 1e18;
	i64 max = -1e18;
	for (int i = 0; i < n; ++ i) {
		auto & [x, y] = c[i];
		if (max >= x) {
			std::cout << ans << "\n";
			return;
		} 

		min = std::min(min, (x - max) * 2);
		max = std::max(max, y);
	}

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

int main() {
	std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
	int t = 1;
	std::cin >> t;
	while (t -- ) {
		solve();
	}
	return 0;
}

D. Root was Built by Love, Broken by Destiny

题意:一个图,你要把它们分成一个二分图,然后两边任意排列点的位置,满足所有边不能相交。有多少方案。

模拟发现,图不仅需要是二分图,还不能有环。
而题目又保证图联通,所以这个图一定是一棵树。
然后一个点和它相邻的点一定在不同侧,为了不和其它边相交,这些点应该连续排列,然后可能这些点和同一侧的其它点也有交集,此时这个点必须放在外侧,所以最多有两个和其它点有边的点,这样两个点可以放在左右两侧,其它点在中间随意排列。
然后每个方案还可以两侧交换,依然是合法方案,所以方案数乘二。
然后因为还有前后关系,我们可以左右翻转一个方案,所以答案又可以乘二。不过需要注意的是如果一侧只有一个点,左右翻转还是一样的方案,此时不能乘二。

代码省略取模类。

点击查看代码
constexpr int P = 1000000007;
using Z = MInt<P>;

struct Comb {
    int n;
    std::vector<Z> _fac;
    std::vector<Z> _invfac;
    std::vector<Z> _inv;
    
    Comb() : n{0}, _fac{1}, _invfac{1}, _inv{0} {}
    Comb(int n) : Comb() {
        init(n);
    }
    
    void init(int m) {
        if (m <= n) return;
        _fac.resize(m + 1);
        _invfac.resize(m + 1);
        _inv.resize(m + 1);
        
        for (int i = n + 1; i <= m; i++) {
            _fac[i] = _fac[i - 1] * i;
        }
        _invfac[m] = _fac[m].inv();
        for (int i = m; i > n; i--) {
            _invfac[i - 1] = _invfac[i] * i;
            _inv[i] = _invfac[i] * _fac[i - 1];
        }
        n = m;
    }
    
    Z fac(int m) {
        if (m > n) init(2 * m);
        return _fac[m];
    }
    Z invfac(int m) {
        if (m > n) init(2 * m);
        return _invfac[m];
    }
    Z inv(int m) {
        if (m > n) init(2 * m);
        return _inv[m];
    }
    Z binom(int n, int m) {
        if (n < m || m < 0) return 0;
        return fac(n) * invfac(m) * invfac(n - m);
    }
} comb;

void solve() {
    int n, m;
    std::cin >> n >> m;
    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);
    }

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

    Z ans = 1;
    for (int i = 0; i < n; ++ i) {
        int cnt = 0;
        for (auto & j : adj[i]) {
            cnt += adj[j].size() == 1;
        }

        if (cnt + 2 < adj[i].size()) {
            ans = 0; 
        } else {
            ans *= comb.fac(cnt);
        }
    }

    ans *= 2;
    int max = 0;
    for (int i = 0; i < n; ++ i) {
        max = std::max(max, (int)adj[i].size());
    }

    if (max != n - 1) {
        ans *= 2;
    }
    std::cout << ans << "\n";
}

int main() {
    std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
    int t = 1;
    std::cin >> t;
    while (t -- ) {
        solve();
    }
    return 0;
}
posted @ 2025-08-08 16:17  maburb  阅读(163)  评论(0)    收藏  举报