Codeforces Round #674 (Div. 3)
C
问题描述
初始状态给你一个 \(1\),有两种操作
- 给其中一个数字增加 1
- 复制其中一个数字并添加到该序列中
问你最少要几次操作,才能得到数字 \(n\)
题解
tag:贪心,枚举
对于两个操作来说,肯定是 先 \(+1\) 再复制,得到的数字更大,(因为这个\(+1\) 操作被复制成了两份),
否则我们可以替换两个操作之间的顺序,得到更大的值,来逼近目标 \(n\)
假定我们一直使用操作一,得到的序列总和为 \(x\)
所以就可以枚举 \(1\sim \sqrt {n}\) ,对于每个\(x\) 计算一次答案,取所有结果的\(min\) 即可
从\(1\) 到 \(x\) 需要\(x-1\) 次操作 ,从 \(x\) 逼近到 \(n\) 需要\(\lceil \frac{(n-x)}{x} \rceil\) 次操作,向上逼近是为了进行余下的第一种操作,比如 \(11,5\) 这些
代码
#include <iostream>
#include <algorithm>
#include <vector>
#include <cstdlib>
#include <cstring>
#include <map>
#include <set>
using namespace std;
#define endl '\n'
#define IO ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
typedef long long LL;
typedef unsigned long long ULL;
typedef double db;
typedef pair<int,int> PII;
const int N = 1e5 + 10;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const db EXP = 1e-9;
int main() {
#ifndef ONLINE_JUDGE
freopen("D:/scode/in.txt","r",stdin);
//freopen("D:/scode/out.txt","w",stdout);
#endif
IO;
int _;
cin >> _;
while(_--) {
int n,ans = INF;
cin >> n;
for(int i = 1;i <= n / i; ++i) {
// 向上取整的简单写法 (n - a + (b - 1)) / b;
ans = min(ans,i - 1 + (n - i + (i - 1)) / i);
}
cout << ans << endl;
}
return 0;
}
D
问题描述
给你一个序列,问你其中有多少个子段的和为 \(0\)
题解
tag:前缀和
考虑前缀和,从左到右一直累加到 \(presum\) 中,如果有两次的\(presum\) 相等,那么中间肯定有一段 所有值相加为 \(0\) 的子段
因为,一直累加\(presum\) ,不可能从\(l\sim r\) \(presum\) 不变,(序列中只有正负数,没有 \(0\)),如果不变,只能是\(+0\) 了
那么我们把重复出现过的\(presum\) 用\(map\) 记录一下就行,注意初始化 \(map[0] = 1\) ,因为重复出现了 \(presum=0\) 也算
代码
#include <iostream>
#include <algorithm>
#include <vector>
#include <cstdlib>
#include <cstring>
#include <map>
#include <set>
using namespace std;
#define endl '\n'
#define IO ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
typedef long long LL;
typedef unsigned long long ULL;
typedef double db;
typedef pair<int,int> PII;
const int N = 1e5 + 10;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const db EXP = 1e-9;
int main() {
#ifndef ONLINE_JUDGE
freopen("D:/scode/in.txt","r",stdin);
//freopen("D:/scode/out.txt","w",stdout);
#endif
IO;
int n,x,ans = 0;
map<LL,int> m;
LL sum = 0;
cin >> n;
m[0] ++;
for(int i = 0;i < n; ++i) {
cin >> x;
sum += x;
if(m[sum] > 0) {
ans ++;
sum = x;
// 清空 map
m.clear();
m[0] ++;
}
m[sum] ++;
}
cout << ans << endl;
return 0;
}
E
问题描述
A 和 B 两个人玩剪刀石头布,一共有 \(n\) 轮
已知 A 出了 \(a_1\) 次石头 \(a_2\) 次剪刀 \(a_3\) 次布,\(a_1+a_2+a_3=n\)
B同样出了 \(b_1,b_2,b_3\) 含义一样 \(b_1+b_2+b_3=n\)
问 A 最小能赢的次数,A最大能赢的次数
题解
tag:贪心,博弈
A最大能赢的次数就是,用 石头去干剪刀,剪刀去干布,布去干石头,即可,取三者的\(min\) 值相加
即 \(min(a_1,b_2) +min(a_2,b_3)+min(a_3,b_1)\)
A最小能赢的次数就是,用石头去干布,然后用剩下的次数去干石头,剩下两个同理
或者计算,总数 - (平局 + 输的最多的次数)= 赢的最少的次数
(平局 + 输的最多的次数) = \(min(a_1,b_1+b_3)\) 去最小即可,因为要计算 \(a_1\) 能被分多少到 \(b_1\) (平局)和 \(b_3\) (输局)
代码
#include <iostream>
#include <algorithm>
#include <vector>
#include <cstdlib>
#include <cstring>
#include <map>
#include <set>
using namespace std;
#define endl '\n'
#define IO ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
typedef long long LL;
typedef unsigned long long ULL;
typedef double db;
typedef pair<int,int> PII;
const int N = 1e5 + 10;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const db EXP = 1e-9;
int n,a[3],b[3];
int main() {
#ifndef ONLINE_JUDGE
freopen("D:/scode/in.txt","r",stdin);
//freopen("D:/scode/out.txt","w",stdout);
#endif
IO;
cin >> n;
cin >> a[0] >> a[1] >> a[2];
cin >> b[0] >> b[1] >> b[2];
int Amx,Ami = 0;
Amx = min(a[0],b[1]) + min(a[1],b[2]) + min(a[2],b[0]);
Ami = n - (min(a[0],b[0] + b[2]) + min(a[1],b[1] + b[0]) + min(a[2],b[2] + b[1]));
cout << Ami << ' ' << Amx << endl;
return 0;
}
F
问题描述
给你一个长度为\(n\) 的字符串,仅包含\(a,b,c,?\) 这四种字符,其中\(?\) 可以变成任意一种其他字符 \(a,b,c\) ,求所有字符串中子序列\(abc\) 的个数
题解
tag:DP
\(f[i][0]\) 表示前 \(i\) 个字符中有多少种序列情况
\(f[i][1]\) 表示前\(i\) 个字符中有多少个 \(a\)
\(f[i][2]\) 表示前\(i\) 个字符中有多少个\(ab\)
\(f[i][3]\) 表示前\(i\) 个字符中有多少个\(abc\)
如果第\(i\) 位为\(a\) ,则 \(f[i][1]=f[i-1][1]+f[i-1][0]\)
其他同理
注意,每一个问号都有三种情况,所以一直累乘
代码是优化成一维的
代码
#include <iostream>
#include <algorithm>
#include <vector>
#include <cstdlib>
#include <cstring>
#include <map>
#include <set>
using namespace std;
#define endl '\n'
#define IO ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
typedef long long LL;
typedef unsigned long long ULL;
typedef double db;
typedef pair<int,int> PII;
const int N = 2e5 + 10;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const db EXP = 1e-9;
LL f[4];
// f0 ?
// f1 a
// f2 ab
// f3 abc
int main() {
#ifndef ONLINE_JUDGE
freopen("D:/scode/in.txt","r",stdin);
//freopen("D:/scode/out.txt","w",stdout);
#endif
IO;
int n;
char ch;
cin >> n;
f[0] = 1;
while(n --) {
cin >> ch;
if(ch == 'a') {
// 前面的 a 和 ? (也能变成 a )
f[1] = (f[1] + f[0]) % mod;
}
else if(ch == 'b') {
// 前面的 ab 和 (前面的 a + 当前的 b) == ab
f[2] = (f[2] + f[1]) % mod;
}
else if(ch == 'c') {
// 前面的 abc 和 (前面的 ab + 当前的 c) == abc
f[3] = (f[3] + f[2]) % mod;
}
else {
// 碰到了 ?
// 从后往前,因为要利用上一层的值,参考01背包的滚动优化
for(int i = 3;i >= 1;--i) {
// 一个 ? 能把当前序列分成三种不同的情况
// 前面都一样是 f[i] 第 i 个是问号,可能会是 f[i]'a',f[i]'b',f[i]'c'
// 所以 前面的f[i] * 3 ,然后加上 当前这个问号变成 f[i - 1]
f[i] = (f[i] * 3 + f[i - 1]) % mod;
}
// 每个 ? 对应一个 * 3
f[0] = f[0] * 3 % mod;
}
}
cout << f[3] << endl;
return 0;
}