
Educational Codeforces Round 195 (Rated for Div. 2) 题解 ABCDE
Problem A
- a < b + 1 a < b + 1 a<b+1 时,无法凑出一个合法的三明治,输出 − 1 -1 −1。
- a > = 2 b a >= 2b a>=2b 时,瓶颈在 b b b ,先凑出 b b b 个 "2A1B",多余的 A 随意处置,此时答案为 b b b。
- 其余情况( 2 b > = a > = b + 1 2b >= a >= b+1 2b>=a>=b+1)时,瓶颈在 a,凑出 a − b − 1 a-b-1 a−b−1 个 "2A1B" 组合,此时剩下 2 b − a + 2 2b-a+2 2b−a+2 个 A 和 2 b − a + 1 2b-a+1 2b−a+1 个 B,恰好凑成一个,答案为 ( a − b − 1 ) + 1 (a-b-1)+1 (a−b−1)+1。
cpp
#include<bits/stdc++.h>
#define int long long
using namespace std;
void solve(){
int a,b;
cin >> a >> b;
if(a < b + 1){
cout << -1 << '\n';
}else if(a >= 2*b){
cout << b << '\n';
}else{
cout << (a-b) << '\n';
}
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
int t = 1;
cin >> t;
while(t --){
solve();
}
}
Problem B
- 最后的结果一定是 01 01 01 交替出现的字符串。因为第一个和最后一个字符固定, 01 01 01 交替串的开头结尾是固定的,只可能因为长度变化得到不同的 01 01 01 交替字符串。
- 当出现连续3个相同字符,操作中间的,总长度可以增加2。
- 当出现子串 010 010 010、 101 101 101 时,操作中间的,总长度可以减少2。
- 什么都不做,总长度不变。
- 综上,答案就是 1 1 1 ~ 3 3 3 的值,判断即可。
cpp
#include<bits/stdc++.h>
#define int long long
using namespace std;
void solve(){
string s;
cin >> s;
int ans = 1;
bool flag1 = false,flag2 = false;
for(int i = 1; i < (int)s.size() - 1; i ++){
if(s[i] == s[i-1] && s[i] == s[i+1]){
flag1 = true;
}
if(s[i] != s[i-1] && s[i] !=s[i+1]){
flag2 = true;
}
}
cout << ans + flag1 + flag2 << '\n';
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
int t = 1;
cin >> t;
while(t --){
solve();
}
}
Problem C
- 本题是经典套路题,用上这俩经典套路就行。
- 1.如何处理长度 > k k k 这个信息?
- 方法是在 i i i 的位置,更新 i − k i - k i−k 位置的信息。
- 不难发现,这样操作后, i − k i-k i−k ~ i i i 这段长度不符合要求的位置,不可能对答案产生任何贡献。
- 2.如何处理被 k k k 整除?
- 方法是按找 m o d k \mod k modk 产生的余数记录信息,对 k k k 同余的两个量,相减之后就可以被 k k k 整除了。
- 完整方法:
- 求前缀和,第 i i i 个位置的前缀和记为 s u m i sumi sumi.
- 维护一个 m a p map map,称作 c n t cnt cnt, c n t s cnts cnts 表示当前状态(不包含长度小于 k k k的区间)下,前缀和 m o d k \mod k modk 余 s s s 的位置个数。
- 在第 i i i 个位置更新第 i − k i - k i−k 的信息到 c n t cnt cnt 里,更新方式为 c n t s u m \[ ( ( i − k ) − 1 ) cnt\[sum\[((i - k) - 1) % k] ++ cnt[sum[((i−k)−1)。
- 以第 i i i 个位置结尾的答案就是 c n t s u m \[ i cntsum\[i % k] cntsum\[i。
cpp
#include<bits/stdc++.h>
#define int long long
using namespace std;
void solve(){
int n,k;
cin >> n >> k;
vector<int> a(n+1);
for(int i = 1; i <= n; i ++){
cin >> a[i];
}
map<int,int> cnt;
vector<int> sum(n+1);
for(int i = 1; i <= n; i ++){
sum[i] = sum[i-1] + a[i];
}
int ans = 0;
for(int i = 1; i <= n; i ++){
if(i > k){
int lastL = i - k;
cnt[sum[lastL-1]%k] ++;
}
if(cnt.find(sum[i]%k) != cnt.end()){
ans += cnt[sum[i]%k];
}
}
cout << ans << '\n';
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
int t = 1;
cin >> t;
while(t --){
solve();
}
}
Problem D
- 出现度数为 0 0 0 的点,不可能合法,答案为 0 0 0,输出 "Brain Melt"。
- 如果一个图的某种染色方案合法,那么将每个点的颜色取反(红变白,白变红)仍然合法,所以答案一定是偶数,不可能为 1 1 1。
- 如果一个连通块中,度数 > = 2 >= 2 >=2 的点只有 1 1 1 个或没有。那么这个图只有 2 2 2 种染色方案。不同连通块间互补干扰,所以两个这样的连通块也能凑出 2 ∗ 2 2 * 2 2∗2 种方案输出 "Many" 了。
- 如果一个连通块中,度数 > = 2 >= 2 >=2 的点不止 1 1 1 个,那么这个图有多种(> 2 2 2 种)染色方案。
cpp
#include<bits/stdc++.h>
#define int long long
using namespace std;
void solve(){
int n,m;
cin >> n >> m;
vector<int> deg(n+1);
vector<vector<int>> v(n+1);
for(int i = 1; i <= m; i ++){
int x,y;
cin >> x >> y;
deg[x] ++,deg[y] ++;
v[x].push_back(y);
v[y].push_back(x);
}
for(int i = 1; i <= n; i ++){
if(deg[i] == 0){
cout << "Brain Melt\n";
return;
}
}
vector<int> done(n+1);
int cnt = 0,cnt2 = 0;
auto dfs = [&](auto &&self,int u) -> void{
done[u] = 1;
if(deg[u] >= 2) cnt2 ++;
for(int i : v[u]){
if(!done[i]){
self(self,i);
}
}
};
int ans = 1;
for(int i = 1; i <= n; i ++){
if(!done[i]){
cnt = 0,cnt2 = 0;
dfs(dfs,i);
if(cnt2 >= 2){
ans *= 4;
}else{
ans *= 2;
}
if(ans > 2){
cout << "Many\n";
return;
}
}
}
cout << ans << '\n';
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
int t = 1;
cin >> t;
while(t --){
solve();
}
}
Problem E
官方题解思路与作者不同,官方题解更巧妙,使用贪心,下述方案思维难度小但码量大,使用DP。
-
先求 bad 子串,再用总数减,显然好一些。
-
很容易猜到使用容斥原理, a ∩ b = a + b − a ∪ b a \cap b = a + b - a \cup b a∩b=a+b−a∪b。本题求的是 a − a ∩ b a - a \cap b a−a∩b。
-
本题也就拆成了,求含 "2025" 的子串、含 "2026" 的子串以及含 "2025" 或 "2026" 的子串三部分。求的方法基本一样,求出一个后复制粘贴就行。
-
可以dp求解。
- 维护 d p l i j dplij dplij,表示考虑以 i i i 结尾的,能够提供目标字符串前 j − 1 j-1 j−1 个字符,不能够提供目标字符串前 j j j 的字符串个数。例如,求解 "2025" 时, d p l 10 2 dpl102 dpl102 表示以 10 10 10 个位置结尾的能够提供 "20" , 不能够提供 "202" 的字符串个数, d p l 10 3 dpl103 dpl103 表示能提供 "202" 的,不能够提供 "2025" 的字符串个数, d p l 10 0 dpl100 dpl100 表示什么都不能提供的字符串个数。
- 转移法是遇到 '2','0','5','6'后,使用 d p l i − 1 j − 1 dpli-1j-1 dpli−1j−1 更新 d p l i j dplij dplij。其余情况只更新 d p i 0 dpi0 dpi0。
- 维护 d p l i j dplij dplij,表示考虑以 i i i 开头的,能够提供目标字符串后 j − 1 j-1 j−1 个字符,不能够提供目标字符串后 j j j 的字符串个数。
- 每个位置的答案,可以用 d p l dpl dpl, d p r dpr dpr 拼凑出。详情看转移方程。
cppans1[i] += (dpl[i-1][0]+1) * dpr[i][4]; ans1[i] += dpl[i-1][1] * (dpr[i][4] + dpr[i][3]); ans1[i] += dpl[i-1][2] * (dpr[i][4] + dpr[i][3] + dpr[i][2]); ans1[i] += dpl[i-1][3] * (dpr[i][4] + dpr[i][3] + dpr[i][2] + dpr[i][1]); ans1[i] += dpl[i-1][4] * (dpr[i][4] + dpr[i][3] + dpr[i][2] + dpr[i][1] + dpr[i][0]);- 需要特别注意空串的情况。 +1 就是用来处理空串的。
-
求出2025后,把代码中的数字5改成6,就求到了2026,把数字5改成5或6,就求到了2025或2026。
cpp
#include<bits/stdc++.h>
#define int long long
using namespace std;
void solve(){
int n;
cin >> n;
string s;
cin >> s;
s = "#" + s;
vector<int> ans1(n+1),ans2(n+1),ans3(n+1);
auto work1 = [&](){
vector<vector<int>> dpl(n+1,vector<int>(5)),dpr(n+2,vector<int>(5));
//2026
for(int i = 1; i <= n; i ++){
dpl[i][0] = dpl[i-1][0];
dpl[i][1] = dpl[i-1][1];
dpl[i][2] = dpl[i-1][2];
dpl[i][3] = dpl[i-1][3];
dpl[i][4] = dpl[i-1][4];
if(s[i] == '2'){
dpl[i][1] += dpl[i-1][0] + 1;
dpl[i][0] = 0;
dpl[i][3] += dpl[i-1][2];
dpl[i][2] = 0;
}else if(s[i] == '0'){
dpl[i][0] += 1;
dpl[i][2] += dpl[i-1][1];
dpl[i][1] = 0;
}else if(s[i] == '5'){
dpl[i][0] += 1;
dpl[i][4] += dpl[i-1][3];
dpl[i][3] = 0;
}else{
dpl[i][0] += 1;
}
}
for(int i = n; i >= 1; i --){
dpr[i][0] = dpr[i+1][0];
dpr[i][1] = dpr[i+1][1];
dpr[i][2] = dpr[i+1][2];
dpr[i][3] = dpr[i+1][3];
dpr[i][4] = dpr[i+1][4];
if(s[i] == '2'){
dpr[i][2] += dpr[i+1][1];
dpr[i][1] = 0;
dpr[i][4] += dpr[i+1][3];
dpr[i][3] = 0;
dpr[i][0] += 1;
}else if(s[i] == '0'){
dpr[i][3] += dpr[i][2];
dpr[i][2] = 0;
dpr[i][0] += 1;
}else if(s[i] == '5'){
dpr[i][1] += dpr[i+1][0] + 1;
dpr[i][0] = 0;
}else{
dpr[i][0] += 1;
}
}
for(int i = 1; i <= n; i ++){
ans1[i] += (dpl[i-1][0]+1) * dpr[i][4];
ans1[i] += dpl[i-1][1] * (dpr[i][4] + dpr[i][3]);
ans1[i] += dpl[i-1][2] * (dpr[i][4] + dpr[i][3] + dpr[i][2]);
ans1[i] += dpl[i-1][3] * (dpr[i][4] + dpr[i][3] + dpr[i][2] + dpr[i][1]);
ans1[i] += dpl[i-1][4] * (dpr[i][4] + dpr[i][3] + dpr[i][2] + dpr[i][1] + dpr[i][0]);
}
};
auto work2 = [&](){
vector<vector<int>> dpl(n+1,vector<int>(5)),dpr(n+2,vector<int>(5));
//2026
for(int i = 1; i <= n; i ++){
dpl[i][0] = dpl[i-1][0];
dpl[i][1] = dpl[i-1][1];
dpl[i][2] = dpl[i-1][2];
dpl[i][3] = dpl[i-1][3];
dpl[i][4] = dpl[i-1][4];
if(s[i] == '2'){
dpl[i][1] += dpl[i-1][0] + 1;
dpl[i][0] = 0;
dpl[i][3] += dpl[i-1][2];
dpl[i][2] = 0;
}else if(s[i] == '0'){
dpl[i][0] += 1;
dpl[i][2] += dpl[i-1][1];
dpl[i][1] = 0;
}else if(s[i] == '6'){
dpl[i][0] += 1;
dpl[i][4] += dpl[i-1][3];
dpl[i][3] = 0;
}else{
dpl[i][0] += 1;
}
}
for(int i = n; i >= 1; i --){
dpr[i][0] = dpr[i+1][0];
dpr[i][1] = dpr[i+1][1];
dpr[i][2] = dpr[i+1][2];
dpr[i][3] = dpr[i+1][3];
dpr[i][4] = dpr[i+1][4];
if(s[i] == '2'){
dpr[i][2] += dpr[i+1][1];
dpr[i][1] = 0;
dpr[i][4] += dpr[i+1][3];
dpr[i][3] = 0;
dpr[i][0] += 1;
}else if(s[i] == '0'){
dpr[i][3] += dpr[i][2];
dpr[i][2] = 0;
dpr[i][0] += 1;
}else if(s[i] == '6'){
dpr[i][1] += dpr[i+1][0] + 1;
dpr[i][0] = 0;
}else{
dpr[i][0] += 1;
}
}
for(int i = 1; i <= n; i ++){
ans2[i] += (dpl[i-1][0]+1) * dpr[i][4];
ans2[i] += dpl[i-1][1] * (dpr[i][4] + dpr[i][3]);
ans2[i] += dpl[i-1][2] * (dpr[i][4] + dpr[i][3] + dpr[i][2]);
ans2[i] += dpl[i-1][3] * (dpr[i][4] + dpr[i][3] + dpr[i][2] + dpr[i][1]);
ans2[i] += dpl[i-1][4] * (dpr[i][4] + dpr[i][3] + dpr[i][2] + dpr[i][1] + dpr[i][0]);
}
};
auto work3 = [&](){
vector<vector<int>> dpl(n+1,vector<int>(5)),dpr(n+2,vector<int>(5));
//2026
for(int i = 1; i <= n; i ++){
dpl[i][0] = dpl[i-1][0];
dpl[i][1] = dpl[i-1][1];
dpl[i][2] = dpl[i-1][2];
dpl[i][3] = dpl[i-1][3];
dpl[i][4] = dpl[i-1][4];
if(s[i] == '2'){
dpl[i][1] += dpl[i-1][0] + 1;
dpl[i][0] = 0;
dpl[i][3] += dpl[i-1][2];
dpl[i][2] = 0;
}else if(s[i] == '0'){
dpl[i][0] += 1;
dpl[i][2] += dpl[i-1][1];
dpl[i][1] = 0;
}else if(s[i] == '6' || s[i] == '5'){
dpl[i][0] += 1;
dpl[i][4] += dpl[i-1][3];
dpl[i][3] = 0;
}else{
dpl[i][0] += 1;
}
}
for(int i = n; i >= 1; i --){
dpr[i][0] = dpr[i+1][0];
dpr[i][1] = dpr[i+1][1];
dpr[i][2] = dpr[i+1][2];
dpr[i][3] = dpr[i+1][3];
dpr[i][4] = dpr[i+1][4];
if(s[i] == '2'){
dpr[i][2] += dpr[i+1][1];
dpr[i][1] = 0;
dpr[i][4] += dpr[i+1][3];
dpr[i][3] = 0;
dpr[i][0] += 1;
}else if(s[i] == '0'){
dpr[i][3] += dpr[i][2];
dpr[i][2] = 0;
dpr[i][0] += 1;
}else if(s[i] == '6' || s[i] == '5'){
dpr[i][1] += dpr[i+1][0] + 1;
dpr[i][0] = 0;
}else{
dpr[i][0] += 1;
}
}
for(int i = 1; i <= n; i ++){
ans3[i] += (dpl[i-1][0]+1) * dpr[i][4];
ans3[i] += dpl[i-1][1] * (dpr[i][4] + dpr[i][3]);
ans3[i] += dpl[i-1][2] * (dpr[i][4] + dpr[i][3] + dpr[i][2]);
ans3[i] += dpl[i-1][3] * (dpr[i][4] + dpr[i][3] + dpr[i][2] + dpr[i][1]);
ans3[i] += dpl[i-1][4] * (dpr[i][4] + dpr[i][3] + dpr[i][2] + dpr[i][1] + dpr[i][0]);
}
};
work1();
work2();
work3();
for(int i = 1; i <= n; i ++){
cout << i * (n - i + 1) - (ans1[i] - (ans1[i] + ans2[i] - ans3[i])) << " \n"[i == n];
}
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
int t = 1;
cin >> t;
while(t --){
solve();
}
}