牛客小白月赛31
B、 A+B (用矩阵代表字符)
原题链接:https://ac.nowcoder.com/acm/contest/10746/B
题目描述
将数字以及加号用字符矩阵的形式进行表示,对应关系如下:
以字符形式给出一个运算式,请你计算结果,同时也请你将结果按照字符的形式输出
输入描述
第一行给出一个正整数 t , 1 ≤ t ≤ 50,代表测试数据的组数
每两组测试数据之间以一个空白行分隔
每组测试数据共有五行字符串表示一个字符串,字符串长度均不大于500且五行长度相等
算式中只会出现数字和加号,保证输入的算式可计算且小于2 ^ 31
输出描述
每组测试数据输出5行字符串表示运算结果,且每两个输出用一个空行隔开
示例
思路
一开始没发现每3列会用1个“ . ”分隔开,一直没看明白样例。
后面就用数组模拟栈,先将字符形式的运算式转变成对应含义的数字和加号存在ch数组里面。
再将得到的数字累加到ans里面,此时ans的数值就是运算结果。
但是题目要求将结果以字符矩阵输出,此时要注意输出和输入一样,每隔3列要输出1个“ . ”分隔开。
代码
//B
#include<iostream>
#include<algorithm>
#include<stdlib.h>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
using namespace std;
#define ll long long
#define getlength(array,len) {len = (sizeof(array) / sizeof(array[0]));}
char book[11][5][3];
//将0~9和+的字符矩阵分别存放在三维数组book里面
void makeTable() {
char temp[11][5][3] = {{{'#', '#', '#'}, {'#', '.', '#'}, {'#', '.', '#'}, {'#', '.', '#'}, {'#', '#', '#'}},
{{'.', '.', '#'}, {'.', '.', '#'}, {'.', '.', '#'}, {'.', '.', '#'}, {'.', '.', '#'}},
{{'#', '#', '#'}, {'.', '.', '#'}, {'#', '#', '#'}, {'#', '.', '.'}, {'#', '#', '#'}},
{{'#', '#', '#'}, {'.', '.', '#'}, {'#', '#', '#'}, {'.', '.', '#'}, {'#', '#', '#'}},
{{'#', '.', '#'}, {'#', '.', '#'}, {'#', '#', '#'}, {'.', '.', '#'}, {'.', '.', '#'}},
{{'#', '#', '#'}, {'#', '.', '.'}, {'#', '#', '#'}, {'.', '.', '#'}, {'#', '#', '#'}},
{{'#', '#', '#'}, {'#', '.', '.'}, {'#', '#', '#'}, {'#', '.', '#'}, {'#', '#', '#'}},
{{'#', '#', '#'}, {'#', '.', '#'}, {'#', '.', '#'}, {'.', '.', '#'}, {'.', '.', '#'}},
{{'#', '#', '#'}, {'#', '.', '#'}, {'#', '#', '#'}, {'#', '.', '#'}, {'#', '#', '#'}},
{{'#', '#', '#'}, {'#', '.', '#'}, {'#', '#', '#'}, {'.', '.', '#'}, {'#', '#', '#'}},
{{'.', '.', '.'}, {'.', '#', '.'}, {'#', '#', '#'}, {'.', '#', '.'}, {'.', '.', '.'}}};
for (int i = 0; i < 11; ++ i) {
for (int j = 0; j < 5; ++ j) {
for (int k = 0; k < 3; ++ k) {
book[i][j][k] = temp[i][j][k];
}
}
}
}
//将最终的运算结果以字符矩阵的形式输出
void output(int num) {
int arr[15] = {1};
int k = 0;
if (num == 0) {
for (int i = 0; i < 5; ++ i) {
printf("%c%c%c\n", book[0][i][0], book[0][i][1], book[0][i][2]);
}
}
else {
//先将每一个位数取出
while(num > 0) {
arr[k ++] = num % 10;
num /= 10;
}
//按行输出
for (int i = 0; i < 5; ++ i) {
for (int j = k - 1; j >= 0; -- j) {
if (j == k - 1)
printf("%c%c%c", book[arr[j]][i][0], book[arr[j]][i][1], book[arr[j]][i][2]);
else printf(".%c%c%c", book[arr[j]][i][0], book[arr[j]][i][1], book[arr[j]][i][2]);
}
printf("\n");
}
}
}
int main() {
makeTable();
int t;
scanf("%d", &t);
while (t --) {
string str[5];
for (int i = 0; i < 5; ++ i) cin>>str[i];
int length = str[0].size(), k = 0;
//字符矩阵转运算式
char ch[200] = {'1'};
for (int i = 0; i < length; i += 4) {
if (str[2][i] == '#' && str[2][i + 1] == '.' && str[3][i] == '#' && str[3][i + 1] == '.')
ch[k ++] = '0';
else if (str[0][i] == '.' && str[0][i + 1] == '.' && str[0][i + 2] == '#')
ch[k ++] = '1';
else if (str[3][i] == '#' && str[3][i + 1] == '.' && str[3][i + 2] == '.')
ch[k ++] = '2';
else if (str[1][i] == '.' && str[2][i] == '#' && str[3][i] == '.' && str[3][i + 1] == '.')
ch[k ++] = '3';
else if (str[0][i] == '#' && str[0][i + 1] == '.' && str[0][i + 2] == '#')
ch[k ++] = '4';
else if (str[3][i] == '.' && str[3][i + 1] == '.' && str[1][i + 2] == '.')
ch[k ++] = '5';
else if (str[1][i + 1] == '.' && str[1][i + 2] == '.' && str[3][i + 1] == '.')
ch[k ++] = '6';
else if (str[2][i] == '#' && str[2][i + 1] == '.' && str[2][i + 2] == '#' && str[3][i] == '.')
ch[k ++] = '7';
else if (str[3][i] == '#' && str[3][i + 1] == '.' && str[3][i + 2] == '#')
ch[k ++] = '8';
else if (str[3][i] == '.' && str[3][i + 1] == '.' && str[3][i + 2] == '#')
ch[k ++] = '9';
else ch[k ++] = '+';
}
//计算运算结果
int num = 0, ans = 0;
for (int i = 0; i < k; ++ i) {
if (ch[i] == '+') {
ans += num;
num = 0;
}
else num = num * 10 + ch[i] - '0';
}
ans += num;
output(ans);
getchar();
printf("\n");
}
return 0;
}
D - 坐标计数 ((x⊕y,∣x−y∣)后x 和 y都为0)
原题链接:https://ac.nowcoder.com/acm/contest/10746/D
题目描述
定义一个坐标变换,坐标 (x,y) 变换后变为(x⊕y,∣x−y∣)。
给定一片矩形区域,计算区域内有多少个整数点在经过有限次变换后变为 (0,0)。
输入描述
输入第一行一个数字 t, 1 ≤ t ≤ 50,表示测试数据组数
接下来一行四个数字 1 <= x1 , y1 , x2 , y2 <= 10 ^ 5 代表给出的矩形区域
(x1 , y1)为矩形区域左下角,(x2 , y2)表示矩形右上角,包含边界上的点。
输入保证有 x1 < x2 , y1 < y2
输出描述
输出区域内满足变换要求的整数点个数
示例
思路
这道题观察样例或者打表都可以发现:矩形面积内的所有点在有限次变换后都能变为(0,0)。
现证明如下:
要使(x,y)在有限次(x⊕y,∣x−y∣)变换中变为(0,0),那么首先,什么时候 x⊕y 会等于 0 ?只有 x == y 的时候。而当 x == y 时,∣x−y∣ 也会等于 0。那么现在问题就转变为有没有可能在有限次变换后 x == y。
但是呢,当把 x 和 y 当成二进制串来考虑相关运算的时候,无论是 x⊕y 还是 ∣x−y∣,如果32位上的二进制数字全都一起考虑相不相等的话,那无疑是比较麻烦的。
那么这里我们可以想,能不能每次先从最后一位比起,假如相同,那么通过右移运算(即:取半)不断缩减,直到 x == y 或者进入死循环。
那么这个时候我们就可以对 x 和 y 进行分类讨论,进而发现:
① 当 x 和 y 同为奇数时,x⊕y 后 x ’ 为偶数,∣x−y∣后 y ’ 也为偶数;
② 当 x 和 y 同为偶数时,x⊕y 后 x ’ 为偶数,∣x−y∣后 y ’ 也为偶数;
③ 当 x 和 y 奇偶性不同时,x⊕y 后 x ’ 为奇数,∣x−y∣后 y ’ 也为奇数,此时便转变为情况①的类型。
综合上面三种情况,我们会发现,无论一开始 x 和 y 奇偶性如何,有限次(x⊕y ,∣x−y∣)后 x ’ 和 y '最终都能成为偶数。而因为我们的目的是要比较 x == y 能不能成立,即:x 和 y 的二进制串各个位数会不会完全相同。那么对于偶数而言,任何偶数最右边的二进制数都为0,进而 x == y 能不能成立就转变为 x / 2 == y / 2能不能成立。
至于 x / 2 和 y / 2 的奇偶性又可以通过有限次(x⊕y ,∣x−y∣)又变为两个都是偶数,循环往复,一直对 x 和 y 取一半,最终 x 和 y 都会变成 0 。
至此证明完毕。
结论:直接计算给定的矩形面积内的点的数目。
代码
//D
#include<iostream>
#include<algorithm>
#include<stdlib.h>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
using namespace std;
#define ll long long
int main() {
int t;
scanf("%d", &t);
while (t --) {
ll a, b, c, d;
scanf("%lld %lld %lld %lld", &a, &b, &c, &d);
ll ans = (c - a + 1) * (d - b + 1);
printf("%lld\n", ans);
}
return 0;
}
E - 解方程 (ax+by=xy)
原题链接:https://ac.nowcoder.com/acm/contest/10746/E
题目描述
给出两个正整数 a,b,计算满足方程 ax+by=x*y 的正整数(x, y) 的组数。
输入描述
输入的第一行有一个正整数 t 测试数据的组数。
每组测试数据在一行中给出两个正整数 a, b。
1 ≤ t ≤ 10 ^ 3
1 ≤ a, b ≤ 10 ^ 6
输出描述
输出一个数字表示答案
保证答案小于2 ^ 31
示例
说明
对于第一组满足条件的(x,y)为(3,3),(4,2)
对于第二组满足条件的(x,y)为(4,8),(5,5),(6,4),(9,3)
思路
ax + by = xy
ax + by + ab = xy + ab
ab = xy + ab - ax - by
ab = (y - a)(x - b)
所以可以发现,要求方程解的个数,其实就是找ab的因子个数。比如说ab = 18,那么(y - a) 对应的可以取到1,2,3,6,9,18,当(y - a)确定时,只有唯一一个(x - b)与之对应,因为a、b是确定的,所以每一组x和y也是唯一的(即不会出现重复)。
那么还有一个问题就是,有没有可能出现 (y - a)= -3, (x - b)= -6 的情况?
乍一看好像可以,但其实不用考虑负数的情况。
比如说a = 3, b = 6,那么ab = (y - a)(x - b)使得要么x = y = 0,要么xy < 0(即x和y有一个为负数),这和题意要求的xy均为正整数矛盾。
综上,答案即ab因子总数。
为了避免超时,这里用了约数个数定理求ab的因子总数。
简单来说,每一个正整数都可以分解成若干个质数相乘,那么对于18 = 2 * 3 * 3,可以看出18由1个2和2个3相乘得到,那么18的因子个数就是(1 + 1)* (2 + 1)= 6个。
代码
//E
#include<iostream>
#include<algorithm>
#include<stdlib.h>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
using namespace std;
#define ll long long
#define getlength(array,len) {len = (sizeof(array) / sizeof(array[0]));}
const int N = 1000030;
//线性筛法求素数
int primes[N], cnt; // primes[]存储所有素数,从0开始存
bool st[N]; // st[x]存储x是否被筛掉,素数--false,合数--true
void get_primes(int n)
{
for (int i = 2; i <= n; ++ i)
{
if (!st[i]) primes[cnt ++ ] = i;
for (int j = 0; primes[j] <= n / i; j ++ )
{
st[primes[j] * i] = true;
if (i % primes[j] == 0) break;
}
}
}
int main() {
int t;
scanf("%d", &t);
get_primes(N - 3); //做素数表
while (t --) {
ll a, b;
scanf("%lld %lld", &a, &b);
ll num = a * b;
int ans = 1;
if (num == 1) printf("%lld\n", ans);
else {
int k = 0;
//用约数个数定理求因子个数
while (num > 1) {
if (num % primes[k] == 0) {
int cnt = 0;
while (num % primes[k] == 0) {
++ cnt;
num /= primes[k];
}
ans *= (cnt + 1);
}
++ k;
}
printf("%lld\n", ans);
}
}
return 0;
}
G - 简单题的逆袭 (x ^ k ≤ y 求max(k))
原题链接:https://ac.nowcoder.com/acm/contest/10746/G
题目描述
给定x,y,找出满足方程 x ^ k ≤ y 的最大的 k
输入描述
第一行一个 t, 1 ≤ t ≤ 300,代表测试数据的组数
每组输入只有一行,包含两个整数 x,y
0 ≤ x, y ≤ 10 ^ 18
输出描述
每个测试数据在一行中输出一个整数k,若k不存在或者无限大,输出 “-1”
示例
思路
先对 x == 0、 x == 1、 y == 0的特殊情况进行判断,再直接试除法去计算 k 的最大值。
y == 0 :指数函数的函数值必定大于等于0,所以 y 不能为0;
x == 0:假设 y = 0,那么 k 可无限大,假如 y > 0,则 k 不存在;
x == 1:假设 y 取非 1 的值,k 不存在;假设 y 取 1,那么 k 可无限大。
代码
//G
#include<iostream>
#include<algorithm>
#include<stdlib.h>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
using namespace std;
#define ll long long
#define getlength(array,len) {len = (sizeof(array) / sizeof(array[0]));}
int main() {
int t;
scanf("%d", &t);
while(t --) {
ll x, y;
scanf("%lld %lld", &x, &y);
if (x == 0 || y == 0 || x == 1) printf("%d\n", -1);
else {
ll cnt = 0;
ll a = x;
while (x <= y) {
++ cnt;
y /= a;
}
printf("%lld\n", cnt);
}
}
return 0;
}
H - 对称之美 (字符串数组能否回文)
原题链接:https://ac.nowcoder.com/acm/contest/10746/H
题目描述
给出n个字符串,从第1个字符串一直到第n个字符串每个串取一个字母来构成一个新字符串,新字符串的第i个字母只能从第i行的字符串中选出,这样就得到了一个新的长度为n的字符串,请问这个字符串是否有可能为回文字符串?
输入描述
第一行一个数字 t , 1 ≤ t ≤ 50, 代表测试数据的组数
每组测试数据先给出一个数字 n,然后接下来n行每行一个只由小写字母组成的字符串 si
1 ≤ n ≤ 100, 1 ≤ ∣s i ∣ ≤ 50
输出描述
在一行中输出 “Yes” or “No”
示例
思路
头尾双指针 i 和 j 分别指向第 i 和倒数第 i 个字符串,也就是需要对应的能构成回文字符的那两行,分别用数组记录这两个字符串各个字符的数目(这里需要注意的是,这两个字符串可能不一定等长)。
记录完毕后,从 0 到 25 遍历是否有数量同时大于 0 的字符,没有则提前结束循环,有则 ++ i 、-- j 接着判断,直到扫描完字符数组或者提前break;
(开一个length == 26的数组,刚好可以存下每个字符的数目,字符对应的索引是:char - ‘a’)
代码
//H
#include<iostream>
#include<algorithm>
#include<stdlib.h>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
using namespace std;
#define ll long long
string arr[110];
int main() {
int t;
scanf("%d", &t);
while (t --) {
int n;
scanf("%d", &n);
for (int i = 0; i < n; ++ i) {
cin>> arr[i];
}
int i, j;
for (i = 0, j = n -1; i < j; ++ i, -- j) { //判断第i和第j行是否有相同的字符
int book1[26] = {0}, book2[26] = {0}; //用book记录这两行各个字母出现的次数
// i 行
int length = arr[i].size();
for (int k = 0; k < length; ++ k) {
++ book1[arr[i][k] - 'a'];
}
//j行
length = arr[j].size();
for (int k = 0; k < length; ++ k) {
++ book2[arr[j][k] - 'a'];
}
int k;
for (k = 0; k < 26; ++ k) {
if (book1[k] > 0 && book2[k] > 0) break; //两行中有相同的字符就break
}
if (k == 26) break; //没找到相同字符,就提前跳出最外层循环,说明此时构不成回文串了
}
if (n == 1) printf("Yes\n");
else {
if (i < j) printf("No\n"); // i < j 说明提前结束循环
else printf("Yes\n");
}
}
return 0;
}
I - 非对称之美 (最长非回文子串)
原题链接:https://ac.nowcoder.com/acm/contest/10746/I
题目描述
给出一个字符串,求最长非回文子字符串的长度
输入描述
在一行中给出一个字符串 s, 1≤∣s∣≤ 10 ^ 7
输出描述
一个整数
示例
思路
不难发现,最长非回文字符串的长度只有 0 ,size() - 1,size() 三种。详细参见代码注释:
代码
// I
#include<iostream>
#include<algorithm>
#include<stdlib.h>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
using namespace std;
#define ll long long
string str;
int main() {
cin>> str;
int l = 0, r = str.size() - 1;
if (r == 0) printf("%d", 0); //只有一个字符也是回文串
else {
char c = str[0];
bool bo = true;
while (l <= r) {
if (str[l] != c || str[r] != c) //判断字符串会不会全是同一个字符
bo = false;
if (str[l] == str[r]) {
++ l;
-- r;
}
else break;
}
// 本身是回文串,且由同一个字符构成
if (l > r && bo) printf("0\n");
// 本身是回文串,但不只由一个字符
else if (l > r) printf("%lld\n", str.size() - 1);
// 本身不是回文串
else printf("%lld\n", str.size());
}
return 0;
}
J - 排列算式
原题链接:https://ac.nowcoder.com/acm/contest/10746/J
题目描述
给出n个数字,对于这些数字是否存在一种计算顺序,使得计算过程中数字不会超过3也不会小于0?
输入描述
首行给出一个正整数 t (1 ≤ t ≤ 1000)代表测试数据组数
每组测试数据第一行一个正整数 n ( 1 ≤ n ≤ 500)
第二行包含n个以空格分隔的数字
输入保证每一个数字都是 −3, −2, −1, +0, +1, +2, +3 的其中一个。
输出描述
每组测试数据输出一行,“Yes” or “No”
示例
说明
第一组依照 +3,−2,+2,−1 的顺序由左至右计算总和,过程会依序算得 3, 1, 3, 2,满足题目要求。
很显然第二组不存在满足要求的计算顺序
思路
因为题目给出的限制是计算过程在【0,3】之间,那么如果 n 个数里面没有 3 或者 -3,只有 ± 1 和 ± 2,那么只要这 n 个数的总和 0 <= sum <= 3,则最终总能找到一种以上计算顺序使得计算过程在【0,3】之间。
因为,假如此时先有了 + 1,无论 - 1 和 -2 的数量如何,如果有 - 1,直接和 + 1抵消,假如没有,在 0 <= sum <= 3 的前提下,完全可以再拿一个 + 2 出来,再用 - 2 进行抵消。(先有了 + 2 也同理,因为 ± 1 和 ± 2 总能不超出【0,3】的范围)
那么我们可以发现是 ± 3 的存在使得计算过程可能不合法。所以现在问题就转变成了:
① sum < 0 || sum > 3 输出“No”
②0 <= sum <= 3,尽可能合法得消除 ± 3,如果( + 3 的数量超过 1 || - 3 的数量超过 0 ),那么输出“No”,反之输出“Yes”。
因为:当 sum == 3 时,允许有一个 + 3,但是不能有 - 3。比如说 sum == 1,但是只剩下 2 2 -3 时,是不可能存在一种正确的操作顺序使得该过程总和不超过3且不小于0的。
代码
#include<bits/stdc++.h>
int main(){
int t;
scanf("%d", &t);
while (t --) {
int n;
scanf("%d", &n);
int a = 0, b = 0, c = 0, x = 0, y = 0, z = 0;
while (n --) {
int k;
scanf("%d", &k);
// 计算各个数的数量,0 可略去
if (k == -1) ++ a;
else if (k == -2) ++ b;
else if (k == -3) ++ c;
else if (k == 1) ++ x;
else if (k == 2) ++ y;
else if (k == 3) ++ z;
}
long long sum = x + 2 * y + 3 * z - a - 2 * b - 3 * c;
if (sum < 0 || sum > 3) {
printf("No\n");
continue;
}
while (z && c) -- z, -- c; // +3 -3
while (z && a && b) -- z, -- a, -- b; //+3 -1 -2
while (z && x && b >= 2) -- z, -- x, b -= 2; // +3 -2 +1 -2
while (z && a >= 3) -- z, a -= 3; //+3 -1 -1 -1
while (c && x && y) -- c, --x, --y; // +1 +2 -3
while (c && a && y >= 2) -- c, -- a, y -= 2; // +2 -1 +2 -3
while (c && x >= 3) -- c, x -= 3; //+1 +1 +1 -3
if (z > 1 || c) printf("No\n");
else printf("Yes\n");
}
return 0;
}