AtCoder Beginner Contest 358
AtCoder Beginner Contest 358 (4/7)
A - Welcome to AtCoder Land
Problem Statement
Takahashi is heading to AtCoder Land. There is a signboard in front of him, and he wants to determine whether it says AtCoder Land.
You are given two strings \(S\) and \(T\) separated by a space. Determine whether \(S=\) AtCoder
and \(T=\) Land
.
简单的字符串比较
code
#include<bits/stdc++.h>
#define int long long
using namespace std;
string s, t;
void solve()
{
cin >> s >> t;
if (s == "AtCoder" && t == "Land")
{
cout << "Yes" << endl;
}
else cout << "No" << endl;
return;
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
solve();
return 0;
}
B - Ticket Counter
Problem Statement
At the entrance of AtCoder Land, there is a single ticket booth where visitors line up to purchase tickets one by one. The purchasing process takes \(A\) seconds per person. Once the person at the front of the line finishes purchasing their ticket, the next person (if any) immediately starts their purchasing process.
Currently, there is no one in line at the ticket booth, and \(N\) people will come to buy tickets one after another. Specifically, the \(i\)-th person will arrive at the ticket booth \(T_i\) seconds from now. If there is already a line, they will join the end of it; if not, they will start the purchasing process immediately. Here, \(T_1 \lt T_2 \lt \dots \lt T_N\).
For each \(i\ (1 \leq i \leq N)\), determine how many seconds from now the \(i\)-th person will finish purchasing their ticket.
在 AtCoder 乐园的入口处,有一个售票亭,游客在此排队逐个购票。每个人的购票过程需要 \(A\) 秒。一旦排在队伍前面的人完成购票,下一个人(如果有的话)就会立即开始他们的购票过程。
目前,售票点没有人排队, \(N\) 人会陆续前来购票。具体来说, \(T_i\) 秒后, \(i\) 人将到达售票点。如果已经有人排队,他们会排在队伍的最后;如果没有,他们会立即开始购票。这里, \(T_1 \lt T_2 \lt \dots \lt T_N\) .
对于每个 \(i\ (1 \leq i \leq N)\) ,确定从现在起 \(i\) /th 的人将在多少秒后完成购票。
排队购票,分成两种情况
- 如果当前到达时间前面有人正在买,等他买完再买
- 前面没人排队就直接去买
code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 110;
int n, m;
int a[N], b[N];
int res;
void solve()
{
cin >> n >> m;
for (int i = 0; i < n; i ++ ) cin >> a[i];
for (int i = 0; i < n; i ++ )
{
if (res <= a[i])
{
b[i] = a[i] + m;
res = a[i] + m;
}
else
{
b[i] = res + m;
res += m;
}
}
for (int i = 0; i < n; i ++ ) cout << b[i] << endl;
return;
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
solve();
return 0;
}
C - Popcorn DFS
Problem Statement
In AtCoder Land, there are \(N\) popcorn stands numbered \(1\) to \(N\). They have \(M\) different flavors of popcorn, labeled \(1, 2, \dots, M\), but not every stand sells all flavors of popcorn.
Takahashi has obtained information about which flavors of popcorn are sold at each stand. This information is represented by \(N\) strings \(S_1, S_2, \dots, S_N\) of length \(M\). If the \(j\)-th character of \(S_i\) is o
, it means that stand \(i\) sells flavor \(j\) of popcorn. If it is x
, it means that stand \(i\) does not sell flavor \(j\). Each stand sells at least one flavor of popcorn, and each flavor of popcorn is sold at least at one stand.
Takahashi wants to try all the flavors of popcorn but does not want to move around too much. Determine the minimum number of stands Takahashi needs to visit to buy all the flavors of popcorn.
在 AtCoder 乐园里,有 \(N\) 个爆米花摊位,编号从 \(1\) 到 \(N\) 。它们有 \(M\) 种不同口味的爆米花,标号为 \(1, 2, \dots, M\) ,但并不是每个摊位都出售所有口味的爆米花。
高桥获得了关于每个摊位出售哪些口味爆米花的信息。这些信息由长度为 \(M\) 的 \(N\) 字符串 \(S_1, S_2, \dots, S_N\) 表示。如果 \(S_i\) 的 \(j\) 个字符是 "o",则表示 \(i\) 摊位出售 \(j\) 口味的爆米花。如果是 "x",则表示 \(i\) 摊位不出售 \(j\) 口味的爆米花。每个摊位至少出售一种口味的爆米花,每种口味的爆米花至少在一个摊位出售。
高桥想尝遍所有口味的爆米花,但又不想走动太多。求高桥至少要去多少个摊位才能买到所有口味的爆米花?
Constraints
- \(N\) and \(M\) are integers.
- \(1 \leq N, M \leq 10\)
- Each \(S_i\) is a string of length \(M\) consisting of
o
andx
. - For every \(i\) \((1 \leq i \leq N)\), there is at least one
o
in \(S_i\). - For every \(j\) \((1 \leq j \leq M)\), there is at least one \(i\) such that the \(j\)-th character of \(S_i\) is
o
.
Sample Input 1
3 5
oooxx
xooox
xxooo
Sample Output 1
2
By visiting the 1st and 3rd stands, you can buy all the flavors of popcorn. It is impossible to buy all the flavors from a single stand, so the answer is \(2\).
Solution
由题,每个摊位至少出售一种口味的爆米花,每种口味的爆米花至少在一个摊位出售,所以最后一定能吃到所有口味爆米花,最坏情况是走遍所有摊位才能吃完所有口味爆米花,这样我们就可以将搜索的深度限制在摊位数量,当搜索深度超过摊位数量就返回,不用再搜索了,从第一个摊位开始搜索,我们可以选择 在该摊位吃爆米花 和 不在摊位吃爆米花,记住不在该摊位吃爆米花时使用的是原参数
code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 20;
int n, m;
string s[N];
int cnt;
int res = 0x7f; //最少前往的摊位数,初始设置为极大值
void bfs(int u, int dep, int cnt, string a) //设置的四个参数分别为 当前所在摊位点编号; 吃过的摊位数(搜索深度); 已吃过的爆米花种数; 利用字符串a记录已吃过的爆米花和未吃过的爆米花
{
string b = a; //保留原始参数 方便后面写跳过该摊位的搜索
int cnt1 = cnt;
//cout << cnt << endl;
if (dep > 11) return; //所有摊位都吃完了还没吃完所有种类 返回 不用考虑了
if (u > n) return; //吃完最后一个摊位还没吃完所有种类 返回
if (cnt == m) //吃完所有种类爆米花 当前吃的摊数与res比较 保留最小值
{
res = min(dep, res);
}
//刚开始没加初始情况特判调了好久
if (dep == 0) //初始情况特判 选择吃的第一个摊位 记录其爆米花种数
{
for (int i = 0; i < m; i ++ )
{
if (a[i] == 'o')
cnt ++ ;
}
}
//cout << cnt << endl;
for (int i = 0; i < m; i ++ ) //将当前所在摊位 与记录已吃过的爆米花字符串a进行比较 若有没吃过的 cnt++ 并更改a
{
if (a[i] == 'x' && s[u][i] == 'o')
{
cnt ++ ;
a[i] = 'o';
}
}
//搜索的核心
bfs(u + 1, dep, cnt1, b); //跳过当前摊位 去下一个摊位 bfs内参数仍为初始原参数
bfs(u + 1, dep + 1, cnt, a); //在当前摊位吃爆米花 并前往下一个摊位
//cout << a << endl;
return;
}
void solve()
{
cin >> n >> m;
for (int i = 0; i < n; i ++ ) cin >> s[i]; //attention 输入的是字符串数组
bool flag = true;
//对一个摊位就能吃完所有爆米花的特判
for (int i = 0; i < n; i ++ )
{
for (int j = 0; j < s[i].size(); j ++ )
{
if (s[i][j] != 'o')
{
flag = false;
break;
}
}
if (flag == true)
{
//cout << "yes";
cout << 1 << endl;
return;
}
}
bfs(0, 0, 0, "xxxxxxxxxxx"); //初始吃过的爆米花种数为0
cout << res << endl;
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
solve();
return 0;
}
D - Souvenirs 双指针 模拟
Problem Statement
A souvenir shop at AtCoder Land sells \(N\) boxes.
The boxes are numbered \(1\) to \(N\), and box \(i\) has a price of \(A_i\) yen and contains \(A_i\) pieces of candy.
Takahashi wants to buy \(M\) out of the \(N\) boxes and give one box each to \(M\) people named \(1, 2, \ldots, M\).
Here, he wants to buy boxes that can satisfy the following condition:
- For each \(i = 1, 2, \ldots, M\), person \(i\) is given a box containing at least \(B_i\) pieces of candy.
Note that it is not allowed to give more than one box to a single person or to give the same box to multiple people.
Determine whether it is possible to buy \(M\) boxes that can satisfy the condition, and if it is possible, find the minimum total amount of money Takahashi needs to pay.
AtCoder 乐园的一家纪念品商店出售 \(N\) 盒子。
这些盒子的编号为 \(1\) 至 \(N\) ,盒子 \(i\) 的价格为 \(A _ i\) 日元,里面有 \(A_ i\) 块糖果。
高桥想从 \(N\) 个盒子中买 \(M\) 个,然后给 \(M\) 个叫 \(1, 2, \ldots, M\) 的人每人一盒。
在这里,他想买的盒子要满足以下条件:
- 对于每个 \(i = 1, 2, \ldots, M\) 人, \(i\) 都能得到至少装有 \(B _ i\) 粒糖果的盒子。
请注意,不允许给一个人多个盒子,也不允许给多个人同一个盒子。
求是否可能买到满足条件的 \(M\) 盒,如果可能,求高桥需要支付的最小总金额。
Constraints
- \(1 \leq M \leq N \leq 2 \times 10^5\)
- \(1 \leq A_i, B_i \leq 10^9\)
- All input values are integers.
Sample Input 1
4 2
3 4 5 4
1 4
Sample Output 1
7
Takahashi can buy boxes \(1\) and \(4\), and give box \(1\) to person \(1\) and box \(4\) to person \(2\) to satisfy the condition.
In this case, he needs to pay \(7\) yen in total, and it is impossible to satisfy the condition by paying less than \(7\) yen, so print \(7\).
Solution
转化题意,1~n这n个盒子,第i个盒子糖果数和价格都是\(A_i\),我们要在这n个盒子里选出m个送给m个人,要求是对于每个人都满足:第i个人收到的盒子装有的糖果数不少于\(B_i\)个,求所需支付的最小金额。不允许给一个人多个盒子,也不允许给多个人同一个盒子。
关键点就在于糖果数和价格都是\(A_i\),我们可以将价格和要求的糖果数捆起来看,既然要求最优价格,只需要让选择的盒子糖果数尽可能小就行
这道题只要求第i个人盒子里的糖果数大于\(B_i\),那我们就将所有盒子从小到大排序,每个人要求的糖果数也从小到大排序,以要求糖果数最小的人为基准,找对应的最小盒子,从这个盒子之后利用双指针,一根指针指盒子,一根指针指糖果数,找满足\(B_i\)条件的盒子
就是特判要麻烦点
code
//我的妈 第一次独立AC D题
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e5 + 10;
int n, m, res;
int a[N], b[N];
void solve()
{
cin >> n >> m;
for (int i = 0; i < n; i ++ ) cin >> a[i];
for (int i = 0; i < m; i ++ ) cin >> b[i];
//对a[] b[]排序
sort(a, a + n);
sort(b, b + m);
//二分找最小的符合b[0]条件的盒子 排好序后的b[0]就是最小要求的糖果数
int l = 0, r = n - 1;
while (l < r)
{
int mid = (l + r) / 2;
if (a[mid] >= b[0]) r = mid;
else l = mid + 1;
}
//cout << r << endl;
//特判 找不满m个盒子的情况
if ((a[r] < b[0]) || (r + m > n) || (a[n - 1] < b[m - 1]))
{
cout << -1 << endl;
return;
}
res += a[r]; //加上这个最小的满足要求的盒子的价格
//特判只需要一个盒子的情况
if (m == 1)
{
cout << res << endl;
return;
}
//双指针找盒子
for (int i = 1, j = 1; i < m; i ++ )
{
while ((a[r + j] < b[i]) && (r + j < n))
{
j ++ ; //由于a b 都是有序数组 不用回退 所以可以用双指针
}
if (r + j == n)
{
cout << -1 << endl;
return;
}
res += a[r + j]; //加上找到的这个盒子
j ++ ; //记住每次取完一个盒子 都要将j指针++ 跳到下一个盒子 不跳的话可能下一次重复取a[r + j]这个盒子 不符合要求 调了好久
}
cout << res << endl;
return;
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
solve();
return 0;
}
E - Alphabet Tiles DP
Problem Statement
AtCoder Land sells tiles with English letters written on them. Takahashi is thinking of making a nameplate by arranging these tiles in a row.
Find the number, modulo \(998244353\), of strings consisting of uppercase English letters with a length between \(1\) and \(K\), inclusive, that satisfy the following conditions:
- For every integer \(i\) satisfying \(1 \leq i \leq 26\), the following holds:
- Let \(a_i\) be the \(i\)-th uppercase English letter in lexicographical order. For example, $a_1 = $
A
, $a_5 = $E
, $a_{26} = $Z
. - The number of occurrences of \(a_i\) in the string is between \(0\) and \(C_i\), inclusive.
- Let \(a_i\) be the \(i\)-th uppercase English letter in lexicographical order. For example, $a_1 = $
AtCoder Land 公司出售写有英文字母的瓷砖。高桥想把这些瓷砖排成一排,做成一个铭牌。
求长度在 \(1\) 和 \(K\) 之间(包括 \(1\) 和 \(K\) )的由大写英文字母组成的字符串中,满足以下条件的字符串的个数(模为 \(998244353\) ):
- 对于满足 \(1 \leq i \leq 26\) 的每个整数 \(i\) ,下面的条件成立:
- 设 \(a_i\) 是按词典顺序排列的 \(i\) 个大写英文字母。例如, $a_1 = $
A
, $a_5 = $E
, $a_{26} = $Z
. - 字符串中 \(a_i\) 的出现次数介于 \(0\) 和 \(C_i\) 之间(包括首尾两次)。
- 设 \(a_i\) 是按词典顺序排列的 \(i\) 个大写英文字母。例如, $a_1 = $
Constraints
- \(1 \leq K \leq 1000\)
- \(0 \leq C_i \leq 1000\)
- All input values are integers.
Sample Input 1
2
2 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Sample Output 1
10
The \(10\) strings that satisfy the conditions are A
, B
, C
, AA
, AB
, AC
, BA
, BC
, CA
, CB
.
Solution
我先对我的排列组合方法持保留意见
先放个思路在这里,后面有时间再实现,当k是26以内 公式为 \(C_{m(存在的字符个数)}^{k}*(A_{k}^{k(有k个选k个 不足k个选总的最大数量)} + A_{k}^{k-1} \ldots A_{k}^{1})+一种字母组成的字符串\)
从存在的字母种类里选k个,再对选出来的字母进行排列组合,还要加上由一种字母组成的字符串(除去单个字母 单个字母在\(A_{k}^{1}\)已经计算)
当k>26,我想的是用递归的做法,还没想好
观察题目,我们是要在数量有限的字母(\(C_i\))中选k个字母排列出来,这道题的模板题P1077 [NOIP2012 普及组] 摆花,也就是带物品限制的01背包问题(多重背包问题)。相比摆花中不同种类的花需按标号的从小到大的顺序依次摆列,本题没有这个限制,字母可以任意排列,所以我们需要用组合数去计算。
我们就可以分别对26个字母中的每一个字母进行考虑,再遍历当前字母的数量,找出所有方法数
-
定义\(dp[i][j]\)表示选择前i个字母组成长度为j的字符串的方案数量
-
状态转移方程:假设遍历到的当前字母要选l个,可以在前面所有种字母已经放置了j-l个字母的方案中,再放置l个当前字母,构成长度为j的字符串,此时也就是将在长度为j的字符串中将前面j-l个字母填入,然后再放入相同的l个当前字母i,也就是说有j个空位,要给前面的j - l个字母放入这些空位中,此时摆放的方案数为\(C_{j}^{j-l}\),产生的总方案数即为\(dp[i][j]=dp[i][j] + dp[i-1][j - l]×C_{j}^{j-l}\),记得方案数\(dp[i][j]\)要用+=,因为最里层循环l的改变每次都会使得\(dp[i][j]\)方案数增加
-
最终答案即为\(\displaystyle\sum_{i=1}^{k}dp[26][i]\)
code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1010, mod = 998244353;
int k;
int a[N];
int dp[30][1010];
int C[1010][1010];
//预处理组合数方式,更快
void init() {
C[0][0] = 1;
for (int i = 1; i <= 1000; i++) {
C[i][0] = C[i][i] = 1;
for (int j = 1; j <= i; j++) {
C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % mod;
}
}
}
// //利用快速幂求逆元
// int qmi(int a, int b)
// {
// int res = 1 % mod;
// while (b)
// {
// if (b & 1)
// res = res * a % mod;
// a = a * a % mod;
// b >>= 1;
// }
// //cout << res << endl;
// return res;
// }
// //求组合数C(x, y) 会超时
// int C(int x, int y)
// {
// int res = 1;
// //先算分子n*(n-1)*...(n-m+1)
// for(int i = x; i >= x - y + 1; i -- )
// {
// res = res * i % mod;
// }
// //cout << res << endl;
// //再用乘法逆元算分母
// for (int i = y; i >= 1; i -- )
// {
// res = res * qmi(i, mod - 2) % mod;
// }
// //cout << res << endl;
// return res;
// }
void solve()
{
cin >> k;
for (int i = 1; i <= 26; i ++ ) cin >> a[i];
dp[0][0] = 1; //其他d[0][i]=0;
for (int i = 1; i <= 26; i ++ )
{
for (int j = 0; j <= k; j ++ )
{
for (int l = 0; l <= min(a[i], j); l ++ ) //选择当前字母的个数上限是 当前字母总个数和当前字符串总长度 的最小值
{
dp[i][j] = (dp[i][j] + dp[i - 1][j - l] * C[j][j - l] % mod) % mod; //记得加上dp[i][j]自身
//cout << dp[i][j+l] << endl;
}
}
}
int res = 0;
for (int i = 1; i <= k; i ++ ) res = (res + dp[26][i]) % mod; //将符合要求的字符串长度加起来
cout << res << endl;
return;
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
init(); //预处理组合数
solve();
return 0;
}