Codeforces Round 970 (Div. 3)

Codeforces Round 970 (Div. 3)

A. Sakurako's Exam

思路

直接枚举即可。

代码

#include <bits/stdc++.h>

using namespace std;

using i64 = long long;

void solve() {

	int a, b;
	cin >> a >> b;

	for (int i = 0; i <= a; i++) {
		for (int j = 0; j <= b; j++) {
			if (i + j * 2 == (a - i) + (b - j) * 2) {
				cout << "YES\n";
				return ;
			}
		}
	}

	cout << "NO\n";

}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);

	int t;
	cin >> t;
	while (t--) {
		solve();
	}

	return 0;
}

B. Square or Not

思路

首先判断一下 \(n\) 是不是完全平方数,然后就看第一行和最后一行是不是全 \(1\),中间的 \(2\sim \sqrt{n}-1\) 行是不是 \(100\dots001\) 这种。

代码

#include <bits/stdc++.h>

using namespace std;

using i64 = long long;

void solve() {

	int n;
	cin >> n;

	string s;
	cin >> s;

	int m = sqrt(n);
	if (m * m != n) {
		cout << "No\n";
		return ;
	}

	string a = string(m, '1'), b = "1" + string(m - 2, '0') + "1";
	if (s.substr(0, m) != a || s.substr(n - m) != a) {
		cout << "No\n";
		return ;
	}
	for (int i = m; i < n - m; i += m) {
		if (s.substr(i, m) != b) {
			cout << "No\n";
			return ;
		}
	}

	cout << "Yes\n";

}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);

	int t;
	cin >> t;
	while (t--) {
		solve();
	}

	return 0;
}

C. Longest Good Array

思路

最优的情况就是相差 \(1,2,3\dots\) 其差值序列为公差为 \(1\) 的等差数列,那么每一项也就是 \(1,2,4,7\dots\) 这样去构造,具体地,仔细观察得出第 \(i\) 项就是 \(1\) 加上差值数列的前 \(i-1\) 项和。

所以我们可以去二分找出 \(r-l\) 的差值里最大的前 \(x\) 项和(解方程也可以),那么就说明它最多可以组成 \(x+1\) 项了。

代码

#include <bits/stdc++.h>

using namespace std;

using i64 = long long;

void solve() {

	i64 a, b;
	cin >> a >> b;

	i64 l = 1, r = 1e9, ans = 0;
	while (l <= r) {
		i64 mid = l + r >> 1;
		if ((1 + mid) * mid <= (b - a) * 2) {
			l = mid + 1;
			ans = mid;
		} else {
			r = mid - 1;
		}
	}

	cout << ans + 1 << '\n';

}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);

	int t;
	cin >> t;
	while (t--) {
		solve();
	}

	return 0;
}

D. Sakurako's Hobby

思路

手玩几个样例大概就能猜出结论,由于是排列的特性,所以最后能够互相到达的点就构成了一个环,那么 \(F(i)\) 就等于这个环里黑点的个数。

可以把这些环看成一个个联通块,用并查集维护黑点的个数即可。

代码

#include <bits/stdc++.h>

using namespace std;

using i64 = long long;

struct UFS {
	int sz;
	vector<int> rank, p, w;
	UFS(int n): sz(n) {
		rank.resize(n + 1);
		p.resize(n + 1);
		w.resize(n + 1);
		for (int i = 0; i <= sz; i++) {
			p[i] = i;
			rank[i] = 0;
		}
	}
	void link(int x, int y) {
		if (x == y)
			return;
		if (rank[x] > rank[y]) {
			p[y] = x;
			w[x] += w[y];
		}
		else {
			p[x] = y;
			w[y] += w[x];
		}
		if (rank[x] == rank[y])
			rank[y]++;
	}
	int find(int x) {
		return x == p[x] ? x : (p[x] = find(p[x]));
	}
	void unin(int x, int y) {
		link(find(x), find(y));
	}
	void compress() {
		for (int i = 0; i < sz; i++)
			find(i);
	}
};

void solve() {

	int n;
	cin >> n;

	vector<int> a(n + 1);
	for (int i = 1; i <= n; i ++) {
		cin >> a[i];
	}

	string s;
	cin >> s;
	s = " " + s;

	UFS ufs(n);
	for (int i = 1; i <= n; i ++) {
		ufs.w[i] = s[i] == '0';
	}

	for (int i = 1; i <= n; i ++) {
		ufs.unin(i, a[i]);
	}

	for (int i = 1; i <= n; i ++) {
		cout << ufs.w[ufs.find(i)] << " \n"[i == n];
	}
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);

	int t;
	cin >> t;
	while (t--) {
		solve();
	}

	return 0;
}

E. Alternating String

思路

分类讨论。

对于 \(|S|\) 为偶数的情况,是不能使用第一个操作的,也就是说它的奇偶上的字符是固定了的,那么这个时候只要分别统计一下奇偶字符上的最多的个数即可,然后用 \(|S|\) 减去它们。

对于 \(|S|\) 为奇数的情况,这个时候删掉中间某个字符,那么这个字符往后的位置,奇偶位置的字符就会互换,所以我们可以维护一个前缀奇偶字符的个数和后缀奇偶字符的个数,然后去枚举中间的字符,那么将这个位置删掉后,前面奇数位上的字符就和后面偶数位上的字符对应,偶数位上反之,这个时候再去统计两数位上最多的字符数即可,然后用 \(|S|\) 减去它们。

代码

#include <bits/stdc++.h>

using namespace std;

using i64 = long long;

void solve() {

	int n;
	cin >> n;

	string s;
	cin >> s;

	if (n & 1) {
		int ans = 0;
		vector pre(2, vector<int>(30));
		auto suf = pre;
		for (int i = 0; i < n; i ++) {
			suf[i & 1][s[i] - 'a'] ++;
		}

		for (int i = 0; i < n; i ++) {
			suf[i & 1][s[i] - 'a']--;
			int res = 0;
			for (int k = 0; k < 2; k ++) {
				int mx = 0;
				for (int j = 0; j < 26; j ++) {
					int t = pre[k][j] + suf[k ^ 1][j];
					if (t > mx) {
						mx = t;
					}
				}
				res += mx;
			}
			pre[i & 1][s[i] - 'a']++;
			ans = max(ans, res);
		}

		ans = n - ans;

		cout << ans << '\n';

	} else {
		int ans = 0;
		vector res(2, vector<int>(30));
		for (int i = 0; i < n; i ++) {
			res[i & 1][s[i] - 'a'] ++;
		}

		for (int i = 0; i < 2; i ++) {
			auto mx = *max_element(res[i].begin(), res[i].end());
			ans += mx;
		}

		ans = n - ans;

		cout << ans << '\n';
	}

}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);

	int t;
	cin >> t;
	while (t--) {
		solve();
	}

	return 0;
}

F. Sakurako's Box

思路

考虑 \(O(n^2)\) 做法,即令 \(S = \sum\limits_{i=1}^{n}\sum\limits_{j=i+1}^n{a_i\times a_j}\),则 \(ans=\frac{S}{\frac{n\times (n-1)}{2}}\)

考虑优化。

展开 \(S\) 得:\(a_1\times (a_2+a_3+\dots+a_n)+a_2\times(a_3+\dots+a_n)\dots\),其实就是枚举 \(i\) 的时候,加上 \(a_i\times (\sum\limits_{j=1}^n a_j-\sum\limits_{j=1}^ia_j)\),而 \(\sum\limits_{j=1}^na_j\) 就是总和,可以预处理,\(\sum\limits_{j=1}^ia_j\) 是当前位置的前 \(i\) 项和,可以在枚举 \(i\) 的时候 \(O(1)\) 计算,那么最后按照上述公式计算即可。

此处用了封装好的取模类重载了部分运算符,实际运算中还是需要用逆元等计算。

代码

#include <bits/stdc++.h>

using namespace std;

using i64 = long long;

//------取模机------//
using i64 = long long;
template<class T>
constexpr T power(T a, i64 b) {
	T res {1};
	for (; b; b /= 2, a *= a) {
		if (b % 2) {
			res *= a;
		}
	}
	return res;
} // 快速幂

constexpr i64 mul(i64 a, i64 b, i64 p) {
	i64 res = a * b - i64(1.L * a * b / p) * p;
	res %= p;
	if (res < 0) {
		res += p;
	}
	return res;
} // 取模乘

template<i64 P>
struct MInt {
	i64 x;
	constexpr MInt() : x {0} {}
	constexpr MInt(i64 x) : x {norm(x % getMod())} {}

	static i64 Mod;
	constexpr static i64 getMod() {
		if (P > 0) {
			return P;
		} else {
			return Mod;
		}
	}
	constexpr static void setMod(i64 Mod_) {
		Mod = Mod_;
	}//只有P<=0, setMod才生效
	constexpr i64 norm(i64 x) const {
		if (x < 0) {
			x += getMod();
		}
		if (x >= getMod()) {
			x -= getMod();
		}
		return x;
	}
	constexpr i64 val() const {
		return x;
	}
	constexpr MInt operator-() const {
		MInt res;
		res.x = norm(getMod() - x);
		return res;
	}
	constexpr MInt inv() const {
		return power(*this, getMod() - 2);
	}
	constexpr MInt &operator*=(MInt rhs) & {
		if (getMod() < (1ULL << 31)) {
			x = x * rhs.x % int(getMod());
		} else {
			x = mul(x, rhs.x, getMod());
		}
		return *this;
	}
	constexpr MInt &operator+=(MInt rhs) & {
		x = norm(x + rhs.x);
		return *this;
	}
	constexpr MInt &operator-=(MInt rhs) & {
		x = norm(x - rhs.x);
		return *this;
	}
	constexpr MInt &operator/=(MInt rhs) & {
		return *this *= rhs.inv();
	}
	friend constexpr MInt operator*(MInt lhs, MInt rhs) {
		MInt res = lhs;
		res *= rhs;
		return res;
	}
	friend constexpr MInt operator+(MInt lhs, MInt rhs) {
		MInt res = lhs;
		res += rhs;
		return res;
	}
	friend constexpr MInt operator-(MInt lhs, MInt rhs) {
		MInt res = lhs;
		res -= rhs;
		return res;
	}
	friend constexpr MInt operator/(MInt lhs, MInt rhs) {
		MInt res = lhs;
		res /= rhs;
		return res;
	}
	friend constexpr std::istream &operator>>(std::istream &is, MInt &a) {
		i64 v;
		is >> v;
		a = MInt(v);
		return is;
	}
	friend constexpr std::ostream &operator<<(std::ostream &os, const MInt &a) {
		return os << a.val();
	}
	friend constexpr bool operator==(MInt lhs, MInt rhs) {
		return lhs.val() == rhs.val();
	}
	friend constexpr bool operator!=(MInt lhs, MInt rhs) {
		return lhs.val() != rhs.val();
	}
	friend constexpr bool operator<(MInt lhs, MInt rhs) {
		return lhs.val() < rhs.val();
	}
};

template<>
i64 MInt<0>::Mod = 1000000007; //只有P<=0, Mod才生效

constexpr int P = 1000000007; //在这设置要用的模数
using Z = MInt<P>;
//------取模机------//

void solve() {

	int n;
	cin >> n;

	Z sum = 0;
	vector<Z> a(n + 1);
	for (int i = 1; i <= n; i ++) {
		cin >> a[i];
		sum += a[i];
	}

	Z res = 0, pre = 0;
	for (int i = 1; i < n; i ++) {
		pre += a[i];
		res += a[i] * (sum - pre);
	}

	Z m = n;
	Z ans = res / (m * (m - 1) / 2);

	cout << ans << '\n';

}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);

	int t;
	cin >> t;
	while (t--) {
		solve();
	}

	return 0;
}

G. Sakurako's Task

思路

要使得 \(mex_k\) 最大,那么每个数都应该尽量地往前靠,那么最后数组应该变成什么样才是最佳的呢?

考虑题目中操作的性质,当 \(a_i\)\(a_j\) 互质时,那么 \(a_i\)\(a_j\) 一定可以互相减去或相加直到变成两个相邻的数;当 \(a_i\)\(a_j\) 不互质时,那么 \(a_i\)\(a_j\) 只能通过减去和相加变成两个相隔 \(\gcd(a_i,a_j)\) 的数。

根据以上结论,我们可以计算出 \(\gcd(a_1,a_2,\dots,a_n)=g\),那么最终的序列一定就只能变成 \(0,g,2g\dots\) 可以证明,无法有更佳答案。

简单证明一下,如果存在更佳答案,那么这个更佳答案一定存在两数之间差距小于 \(g\) ,而 \(\gcd(a_1,a_2,\dots,a_n)\le \gcd(a_i,a_j)(1\le i,j\le n,i\ne j)\),由第二段结论可知,两两最小的差距就是 \(\gcd(a_i,a_j)\),与此相矛盾,证毕。

得到最终序列后,就是枚举找一下第 \(k\) 个在什么位置即可,注意 \(n=1\) 的时候需要特判一下。

代码

#include <bits/stdc++.h>

using namespace std;

using i64 = long long;

void solve() {

	int n, k;
	cin >> n >> k;

	int g = 0;

	vector<int> a(n + 1);
	for (int i = 1; i <= n; i ++) {
		cin >> a[i];
		g = gcd(a[i], g);
	}

	if (n == 1) {
		cout << (k <= a[1] ? k - 1 : k) << '\n';
		return ;
	}

	for (int i = 1; i <= n; i ++) {
		a[i] = (i - 1) * g;
	}

	for (int i = 2; i <= n; i ++) {
		int x = a[i] - a[i - 1] - 1;
		if (k > x) {
			k -= x;
		} else {
			cout << a[i - 1] + k << '\n';
			return ;
		}
	}

	cout << a[n] + k << '\n';

}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);

	int t;
	cin >> t;
	while (t--) {
		solve();
	}

	return 0;
}

H. Sakurako's Test

思路

题目要求最小中位数,那么每个数都要尽可能地减去 \(x\),则这个操作实际上就是 \(a_i \bmod x\),也就是求所有数模 \(x\) 之后的中位数。

对于 \(a_i \bmod x\),可以看成 \(a_i=k\times x+b\),那如果将原数组按值域划分区域 \([0,x),[x,2x)\dots\),那么 \(a_i\) 就属于第 \(k+1\) 个区域,假设当前我们要判断 \(m\) 能不能作为中位数,那就要看 \(b\) 是否小于等于 \(m\) 从而判断 \(a_i\) 是否产生贡献即可。由于所有数都模了 \(x\),所以 \(0\le m< x\),所以我们可以首先做个值域前缀和,然后对每个区域通过作差计算在 \([l,l+m]\) 之中的数有多少,最后将这些贡献统计后再与 \(\frac{n}{2}+1\) 作比较判断 \(m\) 能不能作为中位数。

题目要求最小中位数,那么判断 \(m\) 是否可以作为中位数可以利用以上结论,又知道 \(m\in[0,x)\),那么二分即可。

找出所有可能的 \(x\) 的最小中位数之后,记录一下,那么对于询问就可以 \(O(1)\) 得出了。

代码

#include <bits/stdc++.h>

using namespace std;

using i64 = long long;

void solve() {

	int n, q;
	cin >> n >> q;

	vector<int> pre(n + 1);
	for (int i = 0; i < n; i ++) {
		int x;
		cin >> x;
		pre[x] ++;
	}

	for (int i = 1; i <= n; i ++) {
		pre[i] += pre[i - 1];
	}

	auto check = [&](int x, int m)->bool{
		int res = 0;
		for (int l = 0; l <= n; l += x) {
			int r = min(l + m, n);
			res += pre[r] - (l ? pre[l - 1] : 0);
		}
		return res >= n / 2 + 1;
	};

	vector<int> ans(n + 1);
	for (int x = 1; x <= n; x ++) {
		int l = 0, r = x;
		while (l <= r) {
			int mid = l + r >> 1;
			if (check(x, mid)) {
				r = mid - 1, ans[x] = mid;
			} else {
				l = mid + 1;
			}
		}
	}

	for (int i = 1; i <= q; i ++) {
		int x;
		cin >> x;
		cout << ans[x] << " \n"[i == q];
	}

}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);

	int t;
	cin >> t;
	while (t--) {
		solve();
	}

	return 0;
}
posted @ 2024-09-06 01:33  Ke_scholar  阅读(101)  评论(0编辑  收藏  举报