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


A. Chess Placing

题意:一个长度为\(n\)的数组上有\(n / 2\)个点,你想要移动这些点使得它们的位置奇偶性相同,求最小移动数。

只有两种情况,要么在偶数位置要么在奇数位置,判断两种情况就行。
具体就是从小到大看,拿最小的位置移动到需要移动到的最小位置。

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

    std::ranges::sort(a);

    int ans1 = 0, ans2 = 0;
    for (int i = 1, j = 0; i <= n; i += 2, j += 1) {
    	ans1 += std::abs(i - a[j]);
    }

    for (int i = 2, j = 0; i <= n; i += 2, j += 1) {
    	ans2 += std::abs(i - a[j]);
    }

    std::cout << std::min(ans1, ans2) << "\n";
}

B. Switches and Lamps

题意:有一个\(n \times m\)的01矩阵,你需要删去一行,使得每一列至少有一个1。

枚举这个行即可。

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

	std::vector<int> cnt(m);
	for (int i = 0; i < n; ++ i) {
		for (int j = 0; j < m; ++ j) {
			cnt[j] += s[i][j] == '1';
		}
	}

	for (int i = 0; i < n; ++ i) {
		bool flag = true;;
		for (int j = 0; j < m; ++ j) {
			flag &= cnt[j] - (s[i][j] == '1') > 0;
		}

		if (flag) {
			std::cout << "YES\n";
			return;
		}
	}

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

C. Liebig's Barrels

题意:给你\(n\times k\)个数,你要把他们分成\(n\)组,每组的价值是其中的最小值,且每组的价值相差不超过\(l\)

贪心的想,我们应该让每组的最小值最大。那么我们先排序。
然后找到和\(a_1\)的差小于等于\(l\)的最后一个位置,记为\(p\),那么我们要在\([1, p]\)这些位置里选\(n\)个出来作为最小值。
那么我们从\(p\)开始取,后面有\(n\times k - p\)个数可以分配给前面的数,记为\(cnt\)。那么如果\(cnt \geq k - 1\),我们就可以让\(a_i\)作为最小值。否则我们需要拿前面的数来凑。

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

    std::ranges::sort(a);
    int p = 0;
    while (p + 1 < m && a[p + 1] - a[0] <= l) {
    	++ p;
    }

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

    i64 ans = 0;
    for (int i = p, cnt = m - p - 1; i >= 0;) {
    	if (cnt >= k - 1) {
	    	ans += a[i];
    	 	-- i;
    	 	cnt -= k - 1;
    	} else {
    		int d = k - 1 - cnt;
    		cnt = 0;
    		i -= d;
    		ans += a[i];
    		-- i;
    	}
    }

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

D. Sand Fortress

题意:你有\(n\)个数,和一个无限大的数组,你要每组,\(|a_i - a_{i-1}| \leq 1\)\(a_1 \leq h\),且\(\sum a_i = n\),求满足这些条件的情况下,\(a_i > 0\)\(i\)数量最小是多少

首先观察一下,可以看出放的最后一个数是1,并且如果最大的\(a_i = k\),那么\([1, k]\)里的数都至少出现一次,因为相邻两个数小于等于1,那么\(k\)想要都1,则中间的数都得出现过。
然后一个贪心思想是,\(k\)越大那么大于0的位置就越小。
于是我们二分这个\(k\),找到可行的最大的\(k\),然后求出它的答案。
\(check\)里需要进行分类讨论,如果\(k>h\),则需要先从\(h\)\(k\),然后我们构造一个\(k\)\(1\)的等差序列,如果这些数的和大于\(n\)则不可行。最小剩下的值就都拿去凑\(k\),直到小于\(k\)

点击查看代码
void solve() {
    i64 n, h;
    std::cin >> n >> h;
    auto check = [&](i64 x) -> std::pair<bool, i64> {
    	i128 sum = 0;
    	i64 len = 0;
    	if (x > h) {
    		sum += (i128)(x - h) * (h + x - 1) / 2;
    		len += x - h;
    	}

    	if ((i128)x * (x + 1) / 2 + sum > n) {
    		return {false, 0};
    	}

    	i64 m = n - sum - (i128)x * (x + 1) / 2;
    	return {true, len + x + (m + x - 1) / x};
    };

    i64 l = 1, r = n;
    while (l < r) {
    	i64 mid = l + r + 1 >> 1ll;
    	if (check(mid).first) {
    		l = mid;
    	} else {
    		r = mid - 1;
    	}
    }

    std::cout << check(l).second << "\n";
}

E. Pencils and Boxes

题意:给你\(n\)个数,你要把他们分成若干个集合,每个集合至少有\(m\)个数,并且每个集合的极差不超过\(d\)

先进行排序。
那么每个集合一定是选一些连续的数,因为如果我们选了一些连续的数,然后把这个数分给其它集合,则会导致其它集合的极差变大,显然不优。
那么考虑\(dp\),记\(f_i\)表示以\(i\)结尾选了大于等于\(m\)个数是否可行。
那么我们需要找一个\(j, j < i\),使得\(f_{j-1}\)为1,并且\(i - j + 1 \geq m, a_i - a_j \leq d\)。如果直接暴力找显然超时,可以转换思路,对于每个\(i\)找一个\(j, j > i\),且\(f_{i-1}\)为1,\(j - i + 1 \geq m, a_j - a_i \leq d\)。那么显然如果有\(i' < i\),则\(i'\)可以转移到的\(j\)\(i\)也可以转移到,于是我们记录一个指针\(j\),每次右移动。

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

    std::ranges::sort(a);
    std::vector<int> f(n + 1);
    f[0] = 1;
    for (int i = 1, j = 1; i <= n; ++ i) {
    	if (f[i - 1]) {
    		j = std::max(j, i + m - 1);
    		while (j <= n && a[j] - a[i] <= d) {
    			f[j] = 1;
    			++ j;
    		}
    	}
    }

    if (f[n]) {
    	std::cout << "YES\n";
    } else {
    	std::cout << "NO\n";
    }
}

F. Isomorphic Strings

题意:给你一个字符串,每次求两个长度相等的子串是不是同构的。两个字符串同构,则可以把第一个字符串的某些字符替换为另一个字符串的某个字符,使得和另一个字符串相等。

如果一个字符可以和另一个字符匹配,那么它们它们出现的位置是相同的,则我们可以用26个二进制串来表示每个字符,如果某一位为1代表这个字符在这一位出现过。那么我们把两个字符串的26个二进制串的hash值拿出来比较看是不是一样。

点击查看代码
struct Hash {
	const int mod1 = 1e9 + 7, mod2 = 998244353;
	const int base1 = 131, base2 = 13331;
	std::array<std::vector<int>, 26> h1, h2, p1, p2;
	Hash() {}
	Hash(const std::string & s) {
		init(s);
	}

	void init(const std::string & s) {
		int n = s.size();
		for (int i = 0; i < 26; ++ i) {
			h1[i].resize(n + 1);
			h2[i].resize(n + 1);
			p1[i].resize(n + 1);
			p2[i].resize(n + 1);

			h1[i][0] = h2[i][0] = 0;
			p1[i][0] = p2[i][0] = 1;
			for (int j = 0; j < n; ++ j) {
				h1[i][j + 1] = 1LL * h1[i][j] * base1 % mod1 + (s[j] - 'a' == i) + 1;
				h2[i][j + 1] = 1LL * h2[i][j] * base2 % mod2 + (s[j] - 'a' == i) + 1;
				p1[i][j + 1] = 1LL * p1[i][j] * base1 % mod1;
				p2[i][j + 1] = 1LL * p2[i][j] * base2 % mod2;
			}
		}
	}

	int get1(int l, int r, int ch) {
		return (h1[ch][r] - 1LL * h1[ch][l - 1] * p1[ch][r - l + 1] % mod1 + mod1) % mod1;
	}

	int get2(int l, int r, int ch) {
		return (h2[ch][r] - 1LL * h2[ch][l - 1] * p2[ch][r - l + 1] % mod2 + mod2) % mod2;
	}
};

void solve() {
    int n, q;
    std::cin >> n >> q;
    std::string s;
    std::cin >> s;

    Hash hash(s);

    while (q -- ) {
    	int x, y, len;
    	std::cin >> x >> y >> len;
    	std::vector<std::pair<int, int>> a, b;
    	for (int i = 0; i < 26; ++ i) {
    		a.emplace_back(hash.get1(x, x + len - 1, i), hash.get2(x, x + len - 1, i));
    		b.emplace_back(hash.get1(y, y + len - 1, i), hash.get2(y, y + len - 1, i));
    	}

    	std::ranges::sort(a);
    	std::ranges::sort(b);
    	if (a == b) {
    		std::cout << "YES\n";
    	} else {
    		std::cout << "NO\n";
    	}
    }
}
posted @ 2025-03-28 16:38  maburb  阅读(10)  评论(0)    收藏  举报