Educational Codeforces Round 120 (Rated for Div. 2) A - D
A - Construct a Rectangle#
题意:
给你个数,问能否分割其中一个数,使得四个数组成一个矩形,特殊地,正方形也是矩形。
- 判段一下是否有两数之和等于另一数后者是否有两数相等且第三个数为偶数
#include <bits/stdc++.h>
using namespace std;
void solve() {
int l1,l2,l3; cin >> l1 >> l2 >> l3;
if(l1 + l2 == l3 || l1 + l3 == l2 || l2 + l3 == l1) {
cout << "YES\n";
} else if(l1 == l2 && l3 % 2 == 0|| l1 == l3 && l2 % 2 == 0|| l2 == l3 && l1 % 2 == 0) {
cout << "YES\n";
} else {
cout << "NO\n";
}
}
int main() {
int T; cin >> T;
while(T --) {
solve();
}
}
B - Berland Music#
题意:
给定排列和一个二进制串,构造一个排列使得二进制串中,并且要求最小。
- 贪心
- 首先根据串把分为两大类,小的分配给串中为的位置,大的反之,对应的分配规则即按照原先中大小关系分配给对应大小关系的使得差尽可能的小。
#include <bits/stdc++.h>
using namespace std;
void solve() {
int n; cin >> n;
vector<int>p(n + 1),q(n + 1);
for(int i = 1;i <= n;i ++) {
cin >> p[i];
}
vector<pair<int,int>>a1,a0;
string s; cin >> s;
for(int i = 0;i < s.size();i ++) {
if(s[i] == '1')
a1.push_back({p[i + 1],i + 1});
else
a0.push_back({p[i + 1],i + 1});
}
sort(a1.begin(),a1.end());
sort(a0.begin(),a0.end());
int pos = 0;
for(auto i : a0) {
q[i.second] = ++pos;
};
for(auto i : a1) {
q[i.second] = ++pos;
}
for(int i = 1;i <= n;i ++) {
cout << q[i] << " \n"[i == n];
}
}
int main() {
int T; cin >> T;
while(T --) {
solve();
}
return 0;
}
C - Set or Decrease#
题意:
给定一个数列和一个数。每次操作你可以选择如下两种操作之一:
- 选择一个位置,使得
- 选择两个位置,使得
问需要最少的操作次数使得
- 根据题目所给样例我们也能贪心的想到,更优的方案是摁住数列中最小的数减一定次数后,再把数列中剩余大的数一起减到当前最小数的值
- 将数组排序后,维护一个后缀后,方便每次统计答案
- 二分操作次数,每次时,因为很大,我们不能枚举最小数减多少次,我们直接枚举后缀的几个数变小,剩下的次数直接让最小的减去即可
- 注意枚举的边界,因为我们默认用进行减操作,所以只需枚举的后缀和即可,其实也易得变肯定不是最好的操作
- 复杂度
#include <bits/stdc++.h>
using namespace std;
//1 1 1 2 2 3 6 6 8 10
void solve() {
int n; long long k;
cin >> n >> k;
vector<long long>a(n + 1),s(n + 2,0);
for(int i = 1;i <= n;i ++) cin >> a[i];
sort(a.begin() + 1,a.begin() + 1 + n);
for(int i = n;i >= 1;i --) s[i] = s[i + 1] + a[i];
if(s[1] <= k) {
cout << "0\n";
} else if(n == 1) {
cout << a[1] - k << '\n';
} else {
auto check = [&](long long x) {
for(int i = 0;i < n && i <= x;i ++) {//枚举把后缀的几个数变掉 这么写不要带n因为默认了第一个是用来减的 不过也易得 先减再变肯定比直接变好
long long t = a[1] - (x - i);
long long del = s[n - i + 1] - (t * i) + (x - i);
if(s[1] - k <= del)
return true;
}
return false;
};
long long l = 1,r = s[1] - k,ans;
while(l <= r) {
long long mid = l + r >> 1;
if(check(mid)) {
r = mid - 1; ans = mid;
} else {
l = mid + 1;
}
}
cout << ans << '\n';
}
}
int main() {
// ios::sync_with_stdio(false);
// cin.tie(nullptr);
int T; cin >> T;
while(T --) {
solve();
}
return 0;
}
D - Shuffle#
题意:
给定一个字符串和一个整数,你可以对任意一个包含恰好个连续的子序列的做操作,即重排这个序列中的所有字母,问最后能获得字符串共有多少种?
法Ⅰ直接考虑答案,法Ⅱ是从部分扩展到整体。
法 Ⅰ
- 计数类问题,很明显我们如果直接统计每个有个的子串,必然会有很多重复的情况,我们要做的就是去掉这些重复的情况
- 在长度为的子串中包含个1,那他们的打乱方案就是
- 如下图所示
- 红色序列和蓝色序列均符合要求,但是明显他们中间夹着的绿色的部分会在重复计算,按照容斥原理我们在加上红蓝部分的同时减去绿色部分即可
- 预先处理一下所有的位置,即可完成
#include <bits/stdc++.h>
using namespace std;
//法Ⅰ 容斥计数
const int mod = 998244353;
int C[5010][5010];
int main() {
for(int i = 0;i <= 5000;i ++) {
C[i][0] = 1;
for(int j = 1;j <= i;j ++) {
C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % mod;
}
}//预处理组合数
int n,k; cin >> n >> k;
string s; cin >> s;
vector<int>p;
p.push_back(0);//两端假装放进1 来简化边界计算
for(int i = 0;i < n;i ++) {
if(s[i] == '1') {
p.push_back(i + 1);
}
}
p.push_back(n + 1);
long long ans = 0;
for(int i = k;i <= p.size() - 2;i ++) {
int len = p[i + 1] - 1 - (p[i - k] + 1) + 1;
ans = (ans + C[len][k]) % mod;
if(i > k) {
len = p[i] - 1 - (p[i - k] + 1) + 1;
ans = ((ans - C[len][k - 1]) % mod + mod) % mod;
}
}
if(k == 0 || p.size() - 2 < k) {
cout << "1\n";
} else {
cout << ans << '\n';
}
return 0;
}
法 Ⅱ
- 题目所给数据范围,那么一定会存在做法
- 恰好等于个的打乱方法等于其字串小于等于个的打乱方法的总和,考虑扩展时重复的情况去掉即可
- 从子串向扩展时,考虑什么情况下会重复,即保持和的原样会重复,因为打乱内层时这两个位置不受影响。所以当打乱到这层时,我们必须这两个位置和上次的位置保持不一致才是和内层操作时不一样的方案数贡献
- 分类讨论一下即可,是0强行放1,1强行放0
#include <bits/stdc++.h>
using namespace std;
const int mod = 998244353;
int C[5010][5010],pre[5010];
int main() {
for(int i = 0;i <= 5000;i ++) {
C[i][0] = 1;
for(int j = 1;j <= i;j ++) {
C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % mod;
}
}
int n,k; cin >> n >> k;
string s; cin >> s;
s = ' ' + s;
for(int i = 1;i <= n;i ++) {
pre[i] = pre[i - 1] + (s[i] == '1');
}
if(k == 0 || pre[n] < k) {
cout << "1\n";
return 0;
}
long long ans = 1;//自己本身是一种
for(int i = 1;i <= n;i ++) {
for(int j = i + 1;j <= n;j ++) {
if(pre[j] - pre[i - 1] > k) continue;
int cnt1 = pre[j] - pre[i - 1];
int cnt0 = j - i + 1 - cnt1;
if(s[i] == '0') {
cnt1 --;
} else {
cnt0 --;
}
if(s[j] == '0') {
cnt1 --;
} else {
cnt0 --;
}
if(cnt1 < 0 || cnt0 < 0) continue;
ans = (ans + C[cnt0 + cnt1][cnt1]) % mod;
}
}
cout << ans << '\n';
return 0;
}
标签:
Codeforces题解
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律