Codeforces Round 917 (Div. 2)

A. Least Product

  • 存在 \(a[i] = 0\)\(min = 0\),不需要任何操作。
  • 负数个数为偶数(包括0),\(min = 0\),把任意一个改为 \(0\)
  • 负数个数为奇数,\(min = \prod a[i]\),不需要任何操作。
void solve() {
	int n;
	cin >> n;
	vector<int> b, c, d;
	for(int i = 0; i < n; ++ i) {
		int x;
		cin >> x;
		if(x > 0) b.push_back(x);
		else if(x == 0) c.push_back(x);
		else d.push_back(x);
	}
	if(c.size()) puts("0");
	else if(d.size() % 2 == 0) puts("1\n1 0");
	else puts("0");
}

B. Erase First or Second Letter

枚举每个字符 \(s[i]\),统计以当前字符为第一个元素的方案数。
因为第一个元素已经确定,所以只能删 \([i + 1, j]\)的子串。
先前出现过的字符直接 \(continue\)

void solve() {
	int n;
	string s;
	cin >> n >> s;
	ll ans = 0;
	vector<bool> vis(26, 0);
	for(int i = 0; i < n; ++ i) {
		if(!vis[s[i] - 'a']) {
			vis[s[i] - 'a'] = 1;
			ans += (n - i);
		}
	}
	cout << ans << '\n';
}

C. Watering an Array

考虑把数组清零后怎么贪。
在若干次操作后,整个数组一定是非增的,因此每一天最多有一个位置满足条件。
显然,每两天就进行一次 \(reset\) 操作为最优。


现在回过来处理原始数组。
枚举操作到第 \(i\) 天后再清零。
一次清零最多能得到 \(n\) 个贡献。
因此当 \(i > 2n\) 后,一定不如一开始就清零更优,\(i\) 枚举到 \(2n\) 就行。
注意 \(i\) 要严格小于 \(d\),否则没有多余天数给清零操作。

void solve() {
	int n, k, d;
	cin >> n >> k >> d;
	vector<int> a(n + 1), v(k);
	int ans = 0;
	for(int i = 1; i <= n; ++ i) {
		cin >> a[i];
		if(a[i] == i) ++ ans;
	}
	for(int &x : v) cin >> x;
	ans += (d - 1) >> 1;
	for(int i = 0; i < 2 * n; ++ i) {
		if(i == d - 1) break;
		int res = 0;
		for(int j = 1; j <= v[i % k]; ++ j) ++ a[j];
		for(int j = 1; j <= n; ++ j) if(a[j] == j) ++ res;
		res += (d - i - 2) >> 1;
		ans = max(ans, res);
	}
	cout << ans << '\n';
} 

D. Yet Another Inversions Problem

E. Construct Matrix

一个显然的性质:若得到一组关于 \(k\) 的解,则对答案整个取反后能得到 \(k = n^2 - k\) 的解。

首先考虑无解。

  • \(k\) 为奇数,无解。
  • \(n > 2\)\(k = 2\)\(k = n ^2 - 2\),无解。

我们不妨假设剩下的情况都是有解的。
情况一:\(n=k\),很好构造。

void case1() {	// k == n
	for(int i = 1; i <= n; ++ i) a[i][i] = 1; 
}

情况二:\(n\bmod 4 = 0\)

  • 在一行(列)里添加偶数个 \(1\) 不会对原异或值有任何改动。

根据这条性质,容易想到用 \(\dfrac{k}{4}\)\(2\times 2\) 的单元去填整个网格。

\[\left[ \begin{matrix} 1 & 1\\ 1 & 1 \end{matrix} \right] \]

void case2() {	// k % 4 == 0
	for(int i = 1; i <= n; ++ i) {
		for(int j = 1; j <= n; ++ j) {
			if(!a[i][j] && k) {
				a[i][j] = a[i + 1][j] = a[i][j + 1] = a[i + 1][j + 1] = 1;
				k -= 4;
			}
		}
	}
}

情况三:\(n\bmod 4 = 2\)
去思考如何把情况三转化到情况二。
我们可以在网格的左上角构造如下 \(4\times 4\) 的单元格。

\[\left[ \begin{matrix} 1 & 1 & 0 & 0\\ 1 & 0 & 1 & 0\\ 0 & 1 & 1 & 0\\ 0 & 0 & 0 & 0 \end{matrix} \right] \]

\(k\) 整体减去 \(6\) 后成功转化为情况二。
按照上述构造方法需要考虑 \(k_{max} = n^2 - 9\)
如果 \(k\) 大于其最大值,则令 \(k = n^2 - k\),最后翻转答案。

void Reverse (){
	for(int i = 1; i <= n; ++ i)
		for(int j = 1; j <= n; ++ j)
			a[i][j] ^= 1;
}
void case3() { // k % 4 == 2
	
	bool f = 0;
	if(k > n * n - 9) f = 1, k = n * n - k;
	
	a[1][1] = a[1][2] = a[2][1] = a[2][3] = a[3][2] = a[3][3] = 1; 
	k -= 6;
	
	for(int i = 1; i <= n; ++ i) {
		for(int j = 1; j <= n; ++ j) {
			if(i <= 4 && j <= 4) continue;
			if(!a[i][j] && k) {
				a[i][j] = a[i + 1][j] = a[i][j + 1] = a[i + 1][j + 1] = 1;
				k -= 4;
			}
		}
	}
	if(f) Reverse();
}

F. Construct Tree

先把 \(a\) 排序。

  • 性质一:对于任意两条边 \(e1,e2\),一定存在至少一条路径同时通过这两条边。

容易得到,若 \(a[n] + a[n - 1] > d\),则一定无解。

  • 性质二:若存在边集 \(E\)(不含 \(a[n]\))的子集 \(S\) 满足 \(\sum S = d - a[n]\),则一定有解。

\(S\) 中所有边构成一条链挂在根节点上
对于剩余的边则一个一个挂上去。
注意:此时已经将性质一中的无解情况去掉了,可以证明此时满足条件。。

  • 性质三:设 \(S_1, S_2\) 为边集 \(E\)(不含 \(a[n]\))的两个互不重合的子集。若 \(S_1\)\(S_2\) 的和都大于 \(a[n]\),则一定有解。

\(S_1\) 中所有边构成一条链挂在根节点上,\(S_2\) 同样操作。
对于剩余的边则一个一个挂上去。
可以证明此时满足条件。

对于性质三,我们可以用 \(dp[i][j]\) 表示 \(S_1\) 总和为 \(i\)\(S_2\) 总和为 \(j\) 的方案是否存在。
对于性质二,可以用 \(dp[0][n - a[n]]\) 表示状态。
联想到 \(01\) 背包问题,复杂度 \(O(nd^2)\),会超时。
由于状态是 \(01\) 的,可以用 \(bitset\) 压下位,复杂度 \(O(\dfrac{nd^2}{\omega})\),可以通过。

int n, d, a[N];
bitset<N> dp[N];

void solve() {
	cin >> n >> d;
	for(int i = 1; i <= n; ++ i) cin >> a[i];
	sort(a + 1, a + n + 1);
	if(a[n] + a[n - 1] > d) {
		puts("No");
		return;
	}
	for(int i = 0; i <= d; ++ i) dp[i].reset();
	dp[0][0] = 1;
	for(int i = 1; i < n; ++ i) {
		for(int j = d; j >= 0; -- j) {
			if(j + a[i] <= d) dp[j + a[i]] |= dp[j];
			dp[j] |= dp[j] << a[i];
		}
	}
	bool ok = dp[0][d - a[n]];
	for(int i = a[n]; i <= d - a[n]; ++ i) ok |= dp[i][d - i];
	puts(ok ? "Yes" : "No");
}
posted @ 2023-12-28 21:03  Lu_xZ  阅读(19)  评论(0编辑  收藏  举报