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


A. Treasure Chest

题意:你要经过\(y\)然后到\(x\),你可以让\(x\)最多移动\(k\),求最短距离。

如果\(y \leq x\),答案就是\(x\)
否则尽量把\(x\)\(y\)移就行。

点击查看代码
void solve() {
    int x, y, k;
    std::cin >> x >> y >> k;
    if (y <= x) {
    	std::cout << x << "\n";
    } else {
    	std::cout << std::max(y, 2 * (y - x - k) + x + k) << "\n";
    }
}

B. Points and Minimum Distance

题意:把\(2n\)个数分成\(n\)个坐标,然后按顺序经过每个点,两点之间距离为曼哈顿距离,求距离最短。

单独看一个坐标,我们肯定希望能递增或递减的走,否则走过去一点又走回来肯定走的更多。
那么我们把所有数组排序,因为起点到终点要包含\(n\)个点,所以可以选前\(n\)个为\(x\)坐标,后\(n\)个为\(y\)坐标,这样总距离为这时距离就相当于是相邻两数之差,就等于\(a_n - a_1 + a_{2n} - a_{n+1}\)。可以证明这样走的距离最短,因为交换任意位置都会使得距离增加。

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

    std::sort(a.begin(), a.end());
    std::cout << (a[n - 1] - a[0] + a[2 * n - 1] - a[n]) << "\n";
    for (int i = 0; i < n; ++ i) {
    	std::cout << a[i] << " " << a[i + n] << "\n";
    }
}

C. Torn Lucky Ticket

题意:又\(n\)个长度小于\(5\)的数字,求有多少点对使得\(s_i + s_j\)是幸运的,一个数字是幸运的那么它的长度是偶数并且前半部分数字和等于后半部分数字和。\(s_i + s_j\)就是两个数字拼接起来。

直接枚举每个数字作为较长的那一部分的贡献,分为在前面时和在后面时。用\(map\)统计长度为\(len\)总和为\(s\)的数字的个数。因为每个数字统计的数长度小于自己,所以先把数字按长度排序。

点击查看代码
void solve() {
    int n;
    std::cin >> n;
    std::vector<std::map<int, int> > cnt(6);
    std::vector<std::string> a(n);

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

    auto get = [&](std::string s) -> int {
    	int res = 0;
    	for (auto & c : s) {
    		res = res + c - '0';
    	}
    	return res;
    };

    std::sort(a.begin(), a.end(), [&](std::string & a, std::string & b) {
    	return a.size() < b.size();
    });

    i64 ans = 0;
    for (int i = 0; i < n; ++ i) {
    	int len = a[i].size();
    	for (int j = len; j > len / 2; -- j) {
    		int x = get(a[i].substr(0, j)), y = get(a[i].substr(j));
    		ans += cnt[j * 2 - len][x - y];
    	}

    	for (int j = len; j > len / 2; -- j) {
    		int x = get(a[i].substr(len - j)), y = get(a[i].substr(0, len - j));
    		ans += cnt[j * 2 - len][x - y];
    	}

    	cnt[len][get(a[i])] += 1;
    }

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

D. XOR Construction

题意:给定一个数组\(a\),你要构造一个\(b\),使得\(0\)\(n-1\)都恰好出现一次,并且\(a_i = b_i \oplus b_{i+1}\)

\(b_{i + 1} = a_i \oplus b_i\)。列出前\(i\)个式子发现\(b_i\)等于\(a\)的前缀异或和异或\(b_1\)\(sum_{i - 1} \oplus b_1\),只要确定\(b_1\)的值就可以求出\(b\)。那么我们枚举\(b_1\),看它和这些\(sum\)的最大异或和有没有超过\(n-1\),只要没超过就合法。因为题目保证有解,所以\(sum_i != sum_j, (i != j, 1 <= i, j < n)\),则不可能有两个相等的\(b\),又因为最大不超过\(n-1\),则可以保证恰好每个数出现一次。

点击查看代码
struct TrieWith01 {
	std::vector<std::array<int, 2> > tr;
	int idx;

	int creat() {
		tr.push_back({});
		idx += 1;
		return idx;
	}

	TrieWith01() {
		creat();
		idx = 0;
	}

	void insert(int x) {
		int p = 0;
		for (int i = 30; i >= 0; -- i) {
			int s = x >> i & 1;
			if (!tr[p][s]) {
				tr[p][s] = creat();
			}

			p = tr[p][s];
		}
	}

	int xor_max(int x) {
		int p = 0;
		int res = 0;
		for (int i = 30; i >= 0; -- i) {
			int s = x >> i & 1;
			if (tr[p][!s]) {
				res += 1 << i;
				p = tr[p][!s];
			} else {
				p = tr[p][s];
			}
		}

		return res;
	}
};

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

    TrieWith01 tr;
    int sum = 0;
    for (int i = 0; i + 1 < n; ++ i) {
    	sum ^= a[i];
    	tr.insert(sum);
    }

    std::vector<int> b(n);
    for (int i = 0; i < n; ++ i) {
    	int x = tr.xor_max(i);
    	if (x == n - 1 || (x == n - 2 && i == n - 1)) {
    		b[0] = i;
    		for (int j = 1; j < n; ++ j) {
    			b[j] = b[j - 1] ^ a[j - 1];
    		}
    		break;
    	}
    }

    for (int i = 0; i < n; ++ i) {
    	std::cout << b[i] << " \n"[i == n - 1];
    }
}
posted @ 2025-02-04 21:26  maburb  阅读(22)  评论(0)    收藏  举报