Educational Codeforces Round 86 (Rated for Div. 2) 部分题解

Educational Codeforces Round 86 (Rated for Div. 2)

A. Road To Zero

#include <bits/stdc++.h>
#define ll long long
using namespace std;

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr); cout.tie(nullptr);
	int t; cin >> t;
	while (t--) {
		ll x, y, a, b;
		cin >> x >> y >> a >> b;
		b = min(a + a, b);
		cout << min(x, y) * b + (max(x, y) - min(x, y)) * a << "\n";
	}
}

B. Binary Period

题意:给出一个只由 \(0,1\) 构成的子串 \(t\) ,要求你给出一个原串的构造,并且满足原串长度不大于两倍子串长度,同时原串的循环节最短。

分析:除了全零或者全一的情况,最短的循环节一定是 \(01\) 或者 \(10\) ,因此构造一个 \(10101010\cdots\) 的序列,长度为两倍子串长度。这个构造的正确性可以从最极端的反例:子串 \(000\cdots111\) 证明(前一半是 \(0\),后一半是 \(1\)),不难发现两倍长度必定够了。

#include <bits/stdc++.h>
#define ll long long
using namespace std;

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr); cout.tie(nullptr);
	int t; cin >> t;
	while (t--) {
		string s; cin >> s;
		int zero = 0, one = 0;
		for (auto i : s) {
			if (i == '1') one++;
			else zero++;
		}
		if (!zero || !one) cout << s << '\n';
		else {
			int n = s.length();
			while (n--) cout << "10";
			cout << '\n';
		}
	}
}

C. Yet Another Counting Problem

题意:询问 \([l,r]\) 中有几个数满足 \((x\pmod{a}\bmod{b})\neq(x\pmod{b}\bmod{a})\)

分析:由于 \(a,b\leq200\) ,因此直接枚举模 \(ab\) 的同余类(更准确地说,应该枚举 \(LCM(a,b)\)),求一个 \([0,a*b)\) 的完全剩余系前缀和,就能 \(O(1)\) 分段回答询问了。时间复杂度 \(O(t(ab+q))\)

#include <bits/stdc++.h>
#define ll long long
using namespace std;
ll a, b, q;
vector<ll> pre;

ll calc(ll x) {
	return x / (a * b) * pre[a * b] + pre[x % (a * b)];
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr); cout.tie(nullptr);
	int t; cin >> t;
	while (t--) {
		cin >> a >> b >> q;
		pre.resize(a * b + 1, 0);
		for (int i = 0; i < a * b; ++i)
			pre[i + 1] = pre[i] + (i % a % b != i % b % a);
		while (q--) {
			ll l, r;
			cin >> l >> r;
			cout << calc(r + 1) - calc(l) << ' ';
		}
		cout << '\n';
	}
}

D. Multiple Testcases

题意:给定一个大小为 \(n\) 的数组 \(m_n\) ,并且该数组中的最大元素不超过 \(k\) 。要求给出一种构造,将这个数组分配给多个集合,每个集合中的元素必须满足大小 \(\geq i\) 的元素数量不超过 \(c_i\)

分析:因为 \(c_i\) 是一个单调不增的数列,因此本题就是一个从大到小放的贪心,我们优先向一个集合里放入较大的元素。

#include <bits/stdc++.h>
#define ll long long
using namespace std;

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr); cout.tie(nullptr);
	int n, k;
	cin >> n >> k;
	vector<int> cnt(k + 1);
	vector<int> c(k + 1);
	for (int i = 0; i < n; ++i) {
		int x; cin >> x;
		cnt[x]++;
	}
	for (int i = 1; i <= k; ++i) cin >> c[i];
	vector<vector<int> > res(1);
	int pre = 0, ans = 0;
	for (int i = k; i; --i) {
		int now = 0;
		if (i != k && c[i] == c[i + 1]) now = pre;
		while (cnt[i]) {
			if (now == ans) {
				++ans;
				res.emplace_back();
				continue;
			}
			if (res[now].size() < c[i]) {
				res[now].emplace_back(i);
				--cnt[i];
			}
			else ++now;
		}
		pre = now;
	}
	cout << ans << '\n';
	for (int i = 0; i < ans; ++i) {
		cout << res[i].size();
		for (auto j : res[i]) cout << ' ' << j;
		cout << '\n';
	}
}

E. Placing Rooks

题意:给定一个 \(n\times n\) 的棋盘,要求你在棋盘上放 \(n\) 个车(国际象棋),使得所有格子都能被车在一步内攻击到,询问正好存在 \(k\) 对车能够相互攻击(不能跨过棋子攻击)的棋子摆法有几种。

分析:首先,由于所有格子都需要被车在一步内攻击到,因此该棋盘每一行或者每一列都至少有一个车,由此我们能够立刻推出 \(F(n,0)=n!\) 。并且,由于车不能跨过棋子攻击,因此最多存在 \(k-1\) 对车能够互相攻击,即 \(F(n,k)=0(k\geq n)\)

然后,我们考虑存在 \(k(n>k>0)\) 对车能够相互攻击的情况,不妨假设该棋盘每一行至少有一个车(和每一列至少有一个车的情况是完全对称的,因此结果乘 \(2\) 即可)。我们将这 \(n\) 个车看作图上的点,在能够互相攻击的车之间连线,一共需要连 \(k\) 条线,因此相当于 \(n-k\) 个连通分量,问题转化为将 \(n\) 个元素划分为 \(n-k\) 个子集有几种方法,实际上就是在求第二类斯特林数 \(S^{n-k}_n\) 。然后,由于每一行都至少有一个车,因此只有 \(n-k\) 列中有车,相当于在总共 \(n\) 列的棋盘中选择 \(n-k\) 列,将答案乘上 \(C^{n-k}_{n}\) ;并且我们考虑到这 \(n-k\) 个子集是无序的,因此再乘上 \((n-k)!\) 。因此本题的答案为 \(2S^{n-k}_nC^{n-k}_n(n-k)!\pmod{998244353}\)

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const ll mod = 998244353;
 
ll qpow(ll a, ll b, ll mod) {
	ll ans = 1;
	while (b) {
		if (b & 1) ans = (ans * a) % mod;
		a = (a * a) % mod;
		b >>= 1;
	}
	return ans;
}
 
int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr); cout.tie(nullptr);
	ll n, k, ans = 0;
	cin >> n >> k;
	if (k >= n) {
		cout << 0;
		return 0;
	}
	vector<ll> fac(n + 1, 1);
	for (int i = 2; i <= n; ++i) fac[i] = fac[i - 1] * i % mod;
 
	auto C = [&](ll n, ll m) {
		return fac[n] * qpow(fac[m], mod - 2ll, mod) % mod * qpow(fac[n - m], mod - 2ll, mod) % mod;
	};
 
	for (int i = 0; i <= n - k; ++i) {
		ans += ((n - k - i) % 2 == 0 ? 1ll : -1ll) * C(n - k, i) * qpow(i, n, mod) % mod;
		ans %= mod;
	}
	ans = (ans + mod) % mod;
	ans = (k ? 2ll : 1ll) * ans * C(n, n - k) % mod;
	cout << ans;
}
posted @ 2020-04-27 10:29  st1vdy  阅读(363)  评论(0编辑  收藏  举报