Codeforces Round 1020 (Div. 3)


A. Dr. TC

题意:通过把\(01\)串每一位依次取反得到\(n\)\(01\)串,求这些串里\(1\)的个数。

\(cnt_0\)为串里\(0\)的个数,\(cnt_1\)\(1\)的个数,那么翻转一个\(0\)使得\(1\)的个数加一,翻转一个\(1\)使得\(1\)的个数减一,那么答案就是\(cnt_0 \times (cnt_1 + 1) + cnt_1 \times (cnt_1 - 1)\)

点击查看代码
void solve() {
    int n;
    std::cin >> n;
    std::string s;
    std::cin >> s;
    i64 cnt1 = std::ranges::count(s, '1'), cnt0 = std::ranges::count(s, '0');
    i64 ans = cnt1 * (cnt1 - 1) + cnt0 * (cnt1 + 1);
    std::cout << ans << "\n";
}

B. St. Chroma

题意:给你\(n, x\),构造一个\([0, n - 1]\)的排列,使得\(x\)在前缀\(mex\)里出现的最多。

首先应该把\([0, x - 1]\)放到前面,这样后面的\(mex\)就大于等于\(x\),然后把\(x\)放到最后,中间随便放,则中间这些前缀的\(mex\)都是\(x\)

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

    for (int i = x, j = n - 1; i < n; ++ i) {
    	ans[i] = j -- ;
    }

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

C. Cherry Bomb

题意:两个数组\(a, b\),值域为\([0, k]\)。有\(i, j\in [1, n], a_i + b_i = a_j + b_j\)。现在\(b\)有些位置没填,求合法的\(b\)的个数。

如果\(b\)不全是没填的位置,那么可以确定\(a_i + b_i\)的值,判断是不是每个对的和相等,如果相等判断\(b_i\)有没有超出范围。都合法则只有一种,否则无解。
讨论\(b\)全是\(-1\)的情况。去\(max, min\)\(a\)中最大最小值,那么对于最小值对应的\(b_i\),至少是\(max - min\),最多是\(k\),那么答案就是\(k - (max - min) + 1\)

点击查看代码
void solve() {
    int n, k;
    std::cin >> n >> k;
    std::vector<int> 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];
    }

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

	if (x != -1) {
		for (int i = 0; i < n; ++ i) {
			if (b[i] == -1 && (x - a[i] < 0 || x - a[i] > k)) {
				std::cout << 0 << "\n";
				return;
			}
		}
		std::cout << 1 << "\n";
	} else {
		int min = *std::min_element(a.begin(), a.end());
		int max = *std::max_element(a.begin(), a.end());
		std::cout << k - (max - min) + 1 << "\n";
	}
}

D. Flower Boy

题意:两个数组\(a, b\),从左到右从\(a\)中数,满足第\(j\)个选出的数有\(a_i \geq b_j\)。为了满足操作,你可以在中间插入一个数,求这个数的最小值。

\(pre_i\)为取完\([1, i]\)\(b\)的前缀\(a\)中到的位置,\(suf_i\)表示取完\([i, m]\)的后缀到的位置。
那么如果要插入这个数,那么这个数一定是用来顶替一个\(b_i\)的,我们枚举这个\(b_i\),如果\(pre_{i-1} < suf_{i+1}\)就可以使得我们顶替它后可以把其它数都取完,所有这样的\(b_i\)选一个最小的。

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

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

    std::vector<int> pre(m + 1, n + 1);
    pre[0] = 0;
    for (int i = 1, j = 1; i <= n && j <= m; ++ i) {
    	if (a[i] >= b[j]) {
    		pre[j] = i;
    		++ j;
    	}
    }

    std::vector<int> suf(m + 2, -1);
    suf[m + 1] = n + 1;
    for (int i = n, j = m; i > 0 && j > 0; -- i) {
    	if (a[i] >= b[j]) {
    		suf[j] = i;
    		-- j;
    	}
    }

    if (pre[m] <= n) {
    	std::cout << 0 << "\n";
    	return;
    }

    int ans = 2e9;
    for (int i = 1; i <= m; ++ i) {
    	if (suf[i + 1] > pre[i - 1]) {
    		ans = std::min(ans, b[i]);
    	}
    	// std::cout << pre[i] << " \n"[i == m];
    }

    if (ans == 2e9) {
    	ans = -1;
    }

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

E. Wolf

题意:一个二分查找程序在一个排列的\([l, r]\)子区间上查找\(x\)。这个排列不一定是有序的,所以可能无法正确查询到\(x\)。对于每次查询,你可以选\(k\)个不包含\(x\)的数,将他们的位置重新排列,使得可以查询到\(x\)。求最小的\(k\)

\(p_i\)\(i\)在排列中的位置。
首先如果\(i\)不在\([l, r]\)内,因为不能动\(x\),所以不可能查询到。
如果在,我们模拟一遍,把所有中点取出来,如果当前中点可以让程序走正确的一边,则不用官,否则我们需要找一个数和它交换。具体可以求出,有\(cnt_1\)个数需要和比\(x\)小的数交换,有\(cnt_2\)个数需要和比\(x\)大的数交换。那么分类讨论一下就可以得到需要更改的最少的数。

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

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

    auto get = [&](int l, int r, int x) -> int {
    	int used1 = x, used2 = n - 1 - x;
    	int cnt1 = 0, cnt2 = 0;
    	auto f = [&](auto & self, int l, int r) -> void {
    		if (l >= r) {
    			return;
    		}

    		int mid = l + r >> 1;
    		if (a[mid] == x) {
    			return;
    		}
    		if (p[x] < mid) {
    			if (a[mid] < x) {
    				++ cnt2;
    				-- used1;
    			} else {
    				-- used2;
    			}
    			self(self, l, mid - 1);
    		} else {
    			if (a[mid] > x) {
    				++ cnt1;
    				-- used2;
    			} else {
    				-- used1;
    			}
    			self(self, mid + 1, r);
    		}
    	};

    	f(f, l, r);
    	int res = std::min(cnt1, cnt2);
    	// std::cout << cnt1 << " " << cnt2 << "\n";
    	cnt1 -= res, cnt2 -= res;
    	if (cnt1 > 0) {
    		if (used1 < cnt1) {
    			return -1;
    		}
    		res += cnt1;
    	} else if (cnt2 > 0) {
    		if (used2 < cnt2) {
    			return -1;
    		}
    		res += cnt2;
    	}

    	return res * 2;
    };

    while (q -- ) {
    	int l, r, x;
    	std::cin >> l >> r >> x;
    	-- l, -- r, -- x;
    	if (l > p[x] || r < p[x]) {
    		std::cout << -1 << " \n"[!q];
    		continue;
    	}

    	std::cout << get(l, r, x) << " \n"[!q];
    }
}

F. Goblin

题意:一个\(01\)\(a\),把\(a\)复制\(n\)份组成\(01\)矩阵\(g\),然后把所有\(g[i][i]\)取反。求\(g\)\(0\)联通块的最大大小。

发现可以把数组分成两部分,一种是主对角线左边的三角部分,这个部分的\(g[i][j] = s[j]\),一种是右边的三角部分,同样有\(g[i][j] = s[j]\)。那么我们可以把这些三角的一列当作一个元素,用并查集维护这些元素,每列初始有多少\(0\)也可以求出来。然后把这两部分分别合并一下,然后想要合并这两个三角形,只能通过对角线上的\(0\),那么枚举对角线上的\(0\), 分别和左边的列已经右边的列合并。注意合并的前提是两个元素都是\(0\)
那么就得到了每个联通块的\(0\)的个数。记得开longlong。

点击查看代码
struct DSU {
	std::vector<i64> fa, cnt;
	DSU(int _n) {
		init(_n);
	}

	void init(int _n) {
		fa.assign(_n, 0);
		cnt.assign(_n, 1ll);
		std::iota(fa.begin(), fa.end(), 0);
	}

	int find(int x) {
		return x == fa[x] ? x : fa[x] = find(fa[x]);
	}

	bool merge(int x, int y) {
		x = find(x), y = find(y);
		if (x == y) {
			return false;
		}

		fa[y] = x;
		cnt[x] += cnt[y];
		return true;
	}

	bool same(int x, int y) {
		return find(x) == find(y);
	}

	i64 size(int x) {
		return cnt[find(x)];
	}
};

void solve() {
    int n;
    std::cin >> n;
    std::string s;
    std::cin >> s;
    if (n == 1) {
    	std::cout << (s[0] - '0') << "\n";
    	return;
    }
    auto get = [&](int t, int x) -> int {
    	if (t == 0) {
    		return x;
    	} else if (t == 1) {
    		return n - 2 + x;
    	} else {
    		return 2 * n - 3 + x + 1;
    	}
    };

    DSU d(2 * (n - 1) + n);
    for (int i = 0; i + 1 < n; ++ i) {
    	if (s[i] == '0') {
    		d.cnt[get(0, i)] = n - 1 - i;
    	}
    }

    for (int i = 1; i < n; ++ i) {
    	if (s[i] == '0') {
    		d.cnt[get(1, i)] = i;
    	}
    }

    for (int i = 0; i < n; ++ i) {
    	if (s[i] == '1') {
    		if (i && s[i - 1] == '0') {
    			d.merge(get(0, i - 1), get(2, i));
    		}

    		if (i + 1 < n && s[i + 1] == '0') {
    			d.merge(get(1, i + 1), get(2, i));
    		}
    	}
    }

    for (int i = 0; i + 1 < n - 1; ++ i) {
    	if (s[i] == '0' && s[i + 1] == '0') {
    		d.merge(get(0, i), get(0, i + 1));
    	}
    }

    for (int i = 1; i + 1 < n; ++ i) {
    	if (s[i] == '0' && s[i + 1] == '0') {
    		d.merge(get(1, i), get(1, i + 1));
    	}
    }

    i64 ans = 0;
    for (int i = 0; i < 2 * (n - 1) + n; ++ i) {
    	if (d.find(i) == i) {
    		ans = std::max(ans, d.size(i));
    	}
    }

    std::cout << ans << "\n";
}
posted @ 2025-04-25 01:11  maburb  阅读(431)  评论(0)    收藏  举报