VP Educational Codeforces Round 20


A. Maximal Binary Matrix

题意:构造一个01矩阵,有k个1,并且对于主对角线对称,而且字典序最大。

按行对称的放1即可。主对角线对称的地方就是自己,只用放一个1。

点击查看代码
void solve() {
    int n, k;
    std::cin >> n >> k;
    if (k > n * n) {
    	std::cout << -1 << "\n";
    	return;
    }

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

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

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

B. Distances to Zero

题意:给你一个数组,求每个元素到最近的0的距离。

处理一个前缀和一个后缀的答案,然后两个取最小。

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

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

    for (int i = n; i >= 1; -- i) {
    	suf[i] = suf[i + 1] + 1;
    	if (a[i] == 0) {
    		suf[i] = 0;
    	}
    }

    for (int i = 1; i <= n; ++ i) {
    	int ans = std::min(pre[i], suf[i]);
    	if (ans > n) {
    		ans = -1;
    	}

    	std::cout << ans << " \n"[i == n];
    }
}

C. Maximal GCD

题意:构造一个数组,使得和为kgcd最大。

枚举k的因子x,那么每个数最小就是1×x,2×x,3×...,于是x合法那么kxn(n+1)2。取最大的因子即可。

点击查看代码
void solve() {
    i64 n, m;
    std::cin >> m >> n;
    if ((i128)n * (n + 1) / 2 > m) {
    	std::cout << -1 << "\n";
    	return;
    }

    i64 k = 0;
    std::vector<i64> ans(n);
    for (i64 i = 1; i * i <= m && i <= m / (n * (n + 1) / 2); ++ i) {
    	if (m % i == 0) {
    		k = std::max(k, i);
    		if (m % (m / i) == 0 && m / i <= m / (n * (n + 1) / 2)) {
    			k = std::max(k, m / i);
    		}
    	}
    }

    i64 sum = 0;
	for (int i = 0; i < n; ++ i) {
		ans[i] = k * (i + 1);
		sum += ans[i];
	}

	if (sum < m) {
		ans[n - 1] += m - sum;
	}

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

D. Magazine Ad

题意:一些字符处理成一个个数字,求一个最小的数,使得这些数可以分成不超过k组,且每组的和不小于这个数。

很明显的二分,直接二分答案,然后一个个装数,如果大于mid了就加一组。
这题主要处理字符串的时候要注意最后一个单词是不带句号结束的。

点击查看代码
void solve() {
    int k;
    std::cin >> k;
    std::string line;
    while (line.empty()) {
    	std::getline(std::cin, line);
    }
    std::stringstream ss;
    ss << line;
    std::vector<std::string> s;
    std::string word;
    int max = 0, sum = 0;
    while (ss >> word) {
    	s.push_back(word);
    	max = std::max(max, (int)word.size() + 1);
    	sum += (int)word.size() + 1;
    }

    std::vector<int> a;
    for (auto & t : s) {
		int last = -1;
		for (int i = 0; i < t.size(); ++ i) {
			if (t[i] == '-') {
				a.push_back(i - last);
				last = i;
			}
		}

		a.push_back((int)t.size() - last);
	}

	a.back() -= 1;

    auto check = [&](int len) -> int {
    	int cnt = 0, sum = len;
		for (auto & x : a) {
			if (x > len) {
				return k + 1;
			}

			if (sum + x > len) {
				sum = x;
				++ cnt;
			} else {
				sum += x;
			}
		}


    	return cnt;
    };

    int l = 1, r = sum;
    while (l < r) {
    	int mid = l + r >> 1;
    	if (check(mid) <= k) {
    		r = mid;
    	} else {
    		l = mid + 1;
    	}
    }

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

E. Roma and Poker

题意:给你一个字符串,只包含'W', 'L', D, '?。你要填补?的位置,使得除整个串之外的任意一个前缀,W的个数和L的个数的差的绝对值小于k,并且最终串W的个数和L的个数的差的绝对值等于k

考虑dp,记f[i][j]为前i个串cntWcntL的值为j的情况是否合法。然后这个位置是问号就枚举填哪个字符,否则就只能填给定的字符。同时记录一下当前状态是从前面哪个转移过来的,就可以反退方案。

点击查看代码
void solve() {
    int n, k;
    std::cin >> n >> k;
    std::string s;
    std::cin >> s;
    std::vector f(n + 1, std::vector<int>(2 * k + 1));
    std::vector pre(n + 1, std::vector<int>(2 * k + 1));

    auto change = [&](int i, int j, char c) -> void {
    	int v = c == 'W' ? 1 : c == 'L' ? -1 : 0;
    	f[i + 1][j + v + k] = 1;
    	pre[i + 1][j + v + k] = j;
    };

    f[0][0 + k] = 1;
    for (int i = 0; i < n; ++ i) {
    	for (int j = -k + 1; j < k; ++ j) {
    		if (f[i][j + k] == 0) {
    			continue;
    		}

    		if (s[i] != '?') {
    			change(i, j, s[i]);
    		} else {
    			change(i, j, 'W');
    			change(i, j, 'L');
    			change(i, j, 'D');
    		}
    	}
    }

    if (f[n][k + k] == 0 && f[n][-k + k] == 0) {
    	std::cout << "NO\n";
    } else {
    	int i = n, j = k;
    	if (f[n][-k + k]) {
    		j = -k;
    	}

    	std::string ans;
    	while (i) {
    		int x = pre[i][j + k];
    		if (x == j) {
    			ans += 'D';
    		} else if (x == j - 1) {
    			ans += 'W';
    		} else {
    			ans += 'L';
    		}

    		j = x;
    		-- i;
    	}

    	std::reverse(ans.begin(), ans.end());
    	std::cout << ans << "\n";
    }
}

F. Coprime Subsequences

题意:给你一个数组,求有多少子序列的gcd等于1。

fi表示子序列的gcd等于i的方案数。那么我们预处理每个i在数组里有多少数是他的倍数,记为sumi。那么有2sumi1种方案,但这样会有重复,可能会有一个i都没选,只选了i的倍数的情况,那么我们减去f[j]就行。其中ji的倍数。

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

    const int N = 1e5;
    std::vector<int> sum(N + 1), cnt(N + 1);
    for (int i = 0; i < n; ++ i) {
    	++ cnt[a[i]];
    }

    for (int i = 1; i <= N; ++ i) {
    	for (int j = i; j <= N; j += i) {
    		sum[i] += cnt[j];
    	}
    }

    std::vector<Z> f(N);
    for (int i = N; i >= 1; -- i) {
    	f[i] = power<Z>(2, sum[i]) - 1;
    	for (int j = i + i; j <= N; j += i) {
    		f[i] -= f[j];
    	}
    }

    std::cout << f[1] << "\n";
}
posted @   maburb  阅读(10)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示