比赛链接:

https://codeforces.com/contest/1649

A. Game

题目大意:

\(n\) 个连续的位置,0 表示海洋,1 表示陆地,只可以从一个陆地到另一个陆地上,从一个陆地到相邻的陆地不用花费,但是从第 \(i\) 个陆地到第 \(i + x\) 个陆地需要花费 \(x\),且该移动只能进行一次。

思路:

因为跨陆地移动只能操作一次,所以我们应该从起点先通过临近移动,移动到能到的最远的陆地,然后移动到可以通过临近移动移动到第 \(n\) 个陆地的那个陆地。

代码:

#include <bits/stdc++.h>
using namespace std;
#define IOS() ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define LL long long
LL T = 1, n;
void solve(){
	cin >> n;
	vector <int> a(n);
	int p = 0, q = n - 1;
	for (int i = 0; i < n; ++ i)
		cin >> a[i];
	while (a[p] == a[p + 1] && p < n) p++;
	while (a[q] == a[q - 1] && q > 0) q--;
	if (p < q) cout << q - p << "\n";
	else cout << "0\n";
}
int main(){
	IOS();
	cin >> T;
	while(T--)
		solve();
	return 0;
}

B. Game of Ball Passing

题目大意:

\(n\) 个人玩传球游戏,现在只知道每个人传了几次球,求出最少需要几个球才能完成这个游戏。

思路:

定义 \(s\)\(n\) 个人传球次数的总和,\(m\)\(n\) 中最多的传球次数。
容易知道当 \(m * 2 <= s\) 时,只需要一个球。有一个特殊情况,当所有人的传球次数都是 0 的时候,答案为 0。
\(m * 2 > s\) 时,传球次数就是 \(m * 2 - s\)。因为我们要将这种情况转化为上一种情况,也就是让 \(m * 2 <= s\),所以容易知道传球次数就是 \(m * 2 - s\)

代码:

#include <bits/stdc++.h>
using namespace std;
#define all(x) (x).begin(), (x).end()
#define IOS() ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define LL long long
LL T = 1, n;
void solve(){
	cin >> n;
	vector <LL> a(n);
	for (int i = 0; i < n; ++ i)
		cin >> a[i];
	LL s = accumulate(all(a), 0LL), m = *max_element(all(a));
	if (m == 0) cout << "0\n";
	else if (m * 2 <= s) cout << "1\n";
	else cout << 2 * m - s << "\n";
}
int main(){
	IOS();
	cin >> T;
	while(T--)
		solve();
	return 0;
}

C. Weird Sum

题目大意:

\(n * m\) 的网格,每一个格子有一个数字,同一个数字的格子可以组成一对,它们之间的距离以曼哈顿距离计算,求出网格中每一对的距离之和。

思路:

定义第 \(i\) 种数字总共有 \(len\) 个,分别为 (r_0, c_0),(r_1, c_1),...,(r_{len - 1}, c_{len - 1})。
要求的是 \(\sum_{i = 0}^{len - 1}\sum_{j = i + 1}^{len - 1} \lvert r_i - r_j \rvert + \lvert c_i - c_j \rvert\) = \(\sum_{i = 0}^{len - 1}\sum_{j = i + 1}^{len - 1} \lvert r_i - r_j \rvert + \sum_{i = 0}^{len - 1}\sum_{j = i + 1}^{len - 1} \lvert c_i - c_j \rvert\)
两个的求法其实一样,我们以 \(r\) 为例,如果将 \(r\) 升序排好,那结果就变成 \(\sum_{i = 0}^{len - 1}\sum_{j = i + 1}^{len - 1} (r_j - r_i) = \sum_{i = 0}^{len - 1}\sum_{j = i + 1}^{len - 1} s_j - \sum_{i = 0}^{len - 1}\sum_{j = i + 1}^{len - 1} r_i = \sum_{j = 0}^{len - 1} j * r_j - \sum_{i = 0}^{len - 1}(len - 1 - i) * r_i = \sum_{i = 0}^{2 * i + 1 - len} * r_i\)
按照格子中的数将点分开,然后将它们横纵坐标分别存下来,分别排序,接着按照公式求解。

代码:

#include <bits/stdc++.h>
using namespace std;
#define all(x) (x).begin(), (x).end()
#define IOS() ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define LL long long
#define pb push_back
const int N = 1e5 + 10;
LL n, m, ans, k;
vector <LL> r[N], c[N];
int main(){
	IOS();
	cin >> n >> m;
	for (int i = 0; i < n; ++ i)
		for (int j = 0; j < m; ++ j){
			LL x;
			cin >> x;
			r[x].pb(i);
			c[x].pb(j);
			k = max(x, k);
		}
	for (int i = 1; i <= k; ++ i){
		sort(all(r[i]));
		LL l = 0;
		for (auto x : r[i]){
			ans += (2 * l + 1 - r[i].size()) * x;
			l++;
		}
	}
	for (int i = 1; i <= k; ++ i){
		sort(all(c[i]));
		LL l = 0;
		for (auto x : c[i]){
			ans += (2 * l + 1 - c[i].size()) * x;
			l++;
		}
	}
	cout << ans << "\n";
	return 0;
}

D. Integral Array

题目大意:

\(n\) 个数的一个序列,选择序列中任意两个数 \(x\)\(y\),满足 \(x >= y\)\(\lfloor \frac{x}{y} \rfloor\) 需要在序列中出现。判断序列符不符合要求。

思路:

设倍数为 \(k\),那么 \(y * k <= x < y * (k + 1)\)。通过枚举每一个 \(y\)\(k\),去找序列中有没有 \(x\) 存在,判断存不存在可以通过前缀和,即 \(cnt[y * (k + 1) - 1] - cnt[y * k - 1]\),如果该值大于 0,那么就说明序列中有这个值。

代码:

#include <bits/stdc++.h>
using namespace std;
#define all(x) (x).begin(), (x).end()
#define IOS() ios::sync_with_stdio(false);cin.tie(0);
int T, n, c, a;
void solve(){
	cin >> n >> c;
	vector <int> cnt(c + 1, 0);
	for (int i = 0; i < n; ++ i){
		cin >> a;
		cnt[a]++;
	}
	for (int i = 1; i <= c; ++ i)
		cnt[i] += cnt[i - 1];
	for (int i = 1; i <= c; ++ i)
		if (cnt[i] - cnt[i - 1])
			for (int j = 1; i * j <= c; ++ j)
				if (cnt[min(i * (j + 1) - 1, c)] - cnt[i * j - 1] && cnt[j] == cnt[j - 1]){
					cout << "No\n";
					return;
				}
	cout << "Yes\n";
}
int main(){
	cin >> T;
	while (T--)
		solve();
	return 0;
}

E. Tyler and Strings

题目大意:

已知长为 \(n\) 的序列 \(s\) 和 长为 \(m\) 的序列 \(t\),将 \(s\) 重新排序,使得到的序列字典序要比 \(t\) 小,问有多少种可能,答案对 998244353 取模。(序列的字典序比较与字符串类似,只是将每一位上的字符比较变为了每一位上的数字比较)

思路:

考虑第 \(i\) 位上的情况,当 \(s_i < t_i\) 且前面每一位上的数字都相同时,后面可以任意排序。
剩下的数字有 \(tot\) 个,每一种数字有 \(num_i\),总共有 \(k\) 种,小于 \(t[i]\) 的数字总共有 \(cnt\) 个。
不考虑第 \(i\) 位的总的排序方式为 \(\frac{tot!}{num_1!num_2!...num_k!}\),现在因为 \(s_i < t_i\),所以答案为 \(\frac{(tot - 1)!}{num_1!num_2!...num_k!} * cnt\),显然,暴力计算不可以。
可以发现 \(\frac{(tot - 1)!}{num_1!num_2!...num_k!} * cnt = \frac{tot!}{num_1!num_2!...num_k!} * cnt / tot\),前面的就是剩余数字的总的排序方案,可以通过递推来快速计算。
\(s_i == t_i\) 时,要考虑下一位的可能,同时将 \(s_i\) 这一种数字的个数减一,因为它在这一位上已经被使用了。
综上,要记录当前剩下数字的总数以及每一种数字的数量,因为每一种数字的数量是动态变化的,所以用线段树或者树状数组维护。
有除又有取模,要预处理一下逆元数组来计算。
还有一个特殊情况要考虑,当 \(s\)\(t\) 的前缀时,答案需要 + 1,因为这种情况下,剩余的数字为 0,不会计入答案。

代码:

#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int mod = 998244353, N = 2e5 + 10;
LL n, m, tree[N], finv[N], fact[N], s[N], t[N], cnt[N], ans, res;
LL lowbit(LL k){
	return k & -k;
}
void update(LL x, LL k){
	while (x < N){
		tree[x] += k;
		x += lowbit(x);
	}
}
LL query(LL x){
	LL t = 0;
	while (x != 0){
		t += tree[x];
		x -= lowbit(x);
	}
	return t;
}
LL qp(LL a, LL b){
	if (b == 0) return 1;
	LL ans = 1;
	while (b != 0){
		if (b & 1) ans = (ans * a) % mod;
		b >>= 1;
		a = (a * a) % mod;
	}
	return ans;
}
LL inv(LL x){
	return qp(x, mod - 2);
}
void init(){
	LL maxn = max(n, m);
	fact[0] = 1;
	for (LL i = 1; i <= maxn; ++ i)
		fact[i] = fact[i - 1] * i % mod;
	finv[maxn] = inv(fact[maxn]);
	for (LL i = maxn - 1; i >= 0; -- i)
		finv[i] = finv[i + 1] * (i + 1) % mod;
}
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	cin >> n >> m;
	for (int i = 1; i <= n; ++ i){
		cin >> s[i];
		cnt[s[i]]++;
		update(s[i], 1);
	}
	for (int i = 1; i <= m; ++ i)
		cin >> t[i];
	init();
	res = fact[n];
	for (int i = 1; i < N; ++ i)
		res = ( res * finv[cnt[i]] ) % mod;
	for (LL i = 1; i <= min(m, n); ++ i){
		ans = ( ans + res * query(t[i] - 1) % mod * inv(n - i + 1) ) % mod;
		if (cnt[t[i]] == 0) break;
		res = res * inv(n - i + 1) % mod * cnt[t[i]] % mod;
		cnt[t[i]]--;
		update(t[i], -1);
		if (n < m && i == n) ans = ( ans + 1 ) % mod;
	}
	cout << ans << "\n";
	return 0;
}
posted on 2022-03-08 10:41  Hamine  阅读(133)  评论(0编辑  收藏  举报