AtCoder Beginner Contest 300
B - Same Map in the RPG World
题目大意
给定两个矩阵a和b, 现在可以对b进行两种操作: 一是把矩阵的行向上移一行, 即由1 2 3 4变成2 3 4 1; 二是把矩阵的列向左移一列; 问是否能通过有限次操作让两个矩阵相同;
解题思路
因为行和列的数量都小于30; 所有直接暴力即可;
神秘代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef pair<int, int> PII;
const int N = 35;
char a[N][N];
char b[N][N];
int n,m;
bool check( int y, int z) {
bool f = true;
for (int i=1,j = y; i<=n; i++,j++) {
if (j > n) j = 1;
for (int k = z, h = 1; h<=m; k++, h++) {
if (k > m) k = 1;
if (a[i][h] != b[j][k]) {
f = false;
break;
}
}
if (!f) break;
}
if (f) return true;
else return false;
}
signed main() {
cin >> n >> m;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
cin >> a[i][j];
}
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
cin >> b[i][j];
}
}
char s = a[1][1];
for (int j = 1; j <= n; j++) {
for (int k = 1; k <= m; k++) {
if (b[j][k] == s) {
if (check( j, k)) {
cout << "Yes";
return 0;
}
}
}
}
cout << "No";
return 0;
}
C - Cross
题目大意
给定一个只有'#'和'.'组成的矩阵, 问由'#'组成的X形状的各种大小的都有多少个
解题思路
数量不大, 直接暴力
神秘代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef pair<int, int> PII;
const int N = 110;
char g[N][N];
int dx[] = { 1,1,-1,-1 }, dy[] = { 1,-1,1,-1 };
int n,m;
map<int, int> mp;
void check(int x, int y) {
bool f = true;
int idx = 0;
while (1) {
for (int i = 0; i < 4; i++) {
int a = x + dx[i]*(idx+1), b = y + dy[i]*(idx+1);
if (a<1 || a>n || b<1 || b>m||g[a][b] != '#') {
f = false;
break;
}
}
if (!f) break;
idx++;
}
mp[idx]++;
}
signed main() {
cin >> n >> m;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
cin >> g[i][j];
}
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (g[i][j] == '#') {
check(i, j);
}
}
}
int num = min(n, m);
for (int i = 1; i <= num; i++) {
cout << mp[i] << ' ';
}
return 0;
}
D - AABCC
题目大意
现在有三个质数a,b,c;( a < b < c ) 现在定义一个数为a * a * b * c * c; 给定一个数n, 问不超过n的数里存在多少个这样的数
解题思路
先用线性筛出质数, 然后还是暴力...不过要注意每选一个数都要判定一次, 要不然会tle
注意这里有个坑, 因为n最大为1e12, 所以我们可以只找1e6以内的质数; 但是如果a,b,c都是1e6的数量级, 结果还是会爆long long, c会变成负数也被判定为小于n, 使答案错误; 对于我们在选a和b时, 筛选条件不能只是a* a和a* a* b, 而应该是a* a* a* a* a和a* a* b* b* b, 这样可以有效防止上述情况;
神秘代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef pair<int, int> PII;
const int N = 1e6+10;
int n,num=0;
bool st[N];
int p[N];
void ini() {
for (int i = 2; i <= N; i++) {
if (!st[i]) p[num++] = i;
for (int j = 0; p[j] * i <= N; j++) {
st[i * p[j]] = true;
if (i % p[j]==0) break;
}
}
}
signed main() {
cin >> n;
int idx = 0;
ini();
for (int i = 0; i < num - 2; i++) {
int maxn = 0;
int a = p[i] * p[i] * p[i] * p[i] * p[i];
if (a > n) break;
for (int j = i + 1; j < num - 1; j++) {
int b = p[i] * p[i] * p[j] * p[j] * p[j];
if (b > n) break;
for (int k = j + 1; k < num; k++) {
int c = p[k] * p[k] * p[j] * p[i] * p[i];
if (c <= n) {
idx++;
}
else break;
}
}
}
cout << idx;
return 0;
}
E - Dice Product 3
题目大意
给定一个数m初始值为1, 现有一个骰子, 可以等可能地得到1~6之间的一个数; 然后用m乘这个数来更新m; 现在给定一个数n, 只要m小于n就可以一直进行这个操作, 问m恰好可以等于n的概率为多少, 结果对998244353取模;
解题思路
这个是真不会; 一开始样例都看不明白...题解用的是记忆化搜索(这块我是真不熟...)
我先解释一下样例, 样例中n=6; 我们可以先想到如果想得到6, 那么一定是由6的因数得来的, 比如1 2 3 6; 我们设f6为当前m为6的概率, 则 f6 = ( f1+ f2+ f3+ f6) / 6;把f6移项得到 f6 = ( f1+ f2+ f3) / 5; 同理 f2 = f1/5, f3 = f1/5; 这样就得到 f6 = 7/25 * f1; 因为m初识为1, 所以f1就是1; 故f6 = 7/25; 因为239578645 * 25 ≡ 7 (mod 998244353) 故答案为239578645; 因为5对998244353的逆元为598946612, 所以我们在过程中把除以5变成乘5的逆元即可直接得到答案;
关于什么时候用记忆化搜索, 我感觉是当我们的递推过程中频繁出现之前求过的数, 那我们可以把之前求过的数都存起来, 用的时候直接取出来即可; (因为记忆化搜索地过程朴素地简直不像是一个算法, 所以一直都没怎么思考过它...)
神秘代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef pair<int, int> PII;
const int mod = 998244353;
map<int, int> mp;
int n;
int idx= 598946612;
int check(int x) {
int res=0;
if (mp[x]) return mp[x];
for (int i = 2; i <= 6; i++) {
if (x % i == 0) res= (res+check(x/i))%mod;
}
mp[x] = res * idx % mod;
return mp[x];
}
signed main() {
mp[1] = 1;
cin >> n;
cout<<check(n);
return 0;
}
F - More Holidays
难度: ⭐⭐⭐⭐
题目大意
小莫的一个工作周期的长度是n, 她有一个长度为n的日程表S, x表示工作, o表示休息; 现在小莫可以在m个工作周期中选择k天工作日, 这k天工作日可以转变为休息日; 请问小莫该如何选择这k天可以让他获得的连续休息时间最长; 也就是要找一个长度最长的连续的o;
解题思路
想要得到连续的o, 最优情况肯定是(S的一段后缀) + (若干个S) + (S的一段前缀); 我们可以用前缀和xn[i]表示前i天的工作日有多少个; 中间这若干个S的数量就是k / xn[n]; 接下来就需要分析剩下的1个或者2个工作周期即可; 我们可以选择一个区间, 把这个区间内的x变成o, 一开始想的是二分, 后来发现对于固定的左端点l, 如果想相加x的数量就只需要右移右端点即可; 所以可以直接用双指针线性地走一遍即可, 只要区间内x的数量不大于剩下的k就可以更新最大值;
注意: (S的一段后缀) + (若干个S) + (S的一段前缀) 中的若干个S并非一定是完整的S, 而是对找个长串取长度为若干个S的连续子串;
而且很奇怪的是, 如果我把maxn = max(maxn, j - i + 1) 改为maxn = max(maxn, sum + j - i + 1), 最后输出maxn; 提交后发现会wa; 但是两者明明没有区别, 并且也不会爆longlong; 一直没有找出原因;
神秘代码
#include<bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#define endl '\n'
using namespace std;
const int N = 1e6 + 10, mod = 998244353;
typedef pair<int, int> PII;
int n, m, res;
int xn[N];
signed main() {
int k;
cin >> n >> m >> k;
string s;
cin >> s;
s = '0' + s + s;
for (int i = 1; i <= 2 * n; i++) xn[i] = xn[i - 1] + (s[i] == 'x');
int num = k / xn[n];
m -= num;
k %= xn[n];
int sum = num * n;
int t = n + n * (m > 1);
int maxn = 0;
for (int i = 1,j=1; i <= n; i++) {
while (j <= t && xn[j+1] - xn[i - 1] <= k) j++;
if(m>0) maxn = max(maxn, j - i +1 );
}
cout << sum + maxn;
return 0;
}