A. 完全平方数的尾巴
题目描述
我们把一个能被表示成某个整数的平方的数称为完全平方数。
例如 4 = 2 ∗ 2,16 = 4 ∗ 4,所以4,16是完全平方数。
现在输入一个整数为 x (0 ≤ x ≤ 999),请聪明的你判断它是不是由某个完全平方数对 1000 取模得到的呢。
示例1
输入
24
输出
true
说明
1024 = 32 ∗ 32
24 = 1024 mod 1000
解法:暴力搜索
思路分析
我们知道完全平方数对 k 取模的值,是 k 个一循环的。即对于 f(x) = (x * x) % k,函数值 k 个一循环。
本题对 1000 取模,因此遍历 1 - 999 的完全平方数模 1000 的值,若都不为 x,则返回 false。
代码实现
public boolean solve (int x) {
for(int i = 1; i < 1000; i++) {
if(i * i % 1000 == x) return true;
}
return false;
}
B. 牛牛的字符串
题目描述
牛牛有一个长度为 N 的由小写字母组成的字符串 S,还有一个整数 K。在每一步中,牛牛都可以选择一个位置 i,并在 i 和 i + K 处交换字符(i + K < N)并且 $S_i 备注: 输入 输出 说明 我们看一下交换条件:$S_i 因此对于序列: i, i + k, i + 2k, …。可以交换的次数 = 顺序对的个数。 而字符串中总共有 k 个上述序列。故而求解这 k 个序列的 顺序对个数的求解方法和逆序对个数的求解方法相同,经典的方法有归并排序和离散化树状数组。不过本题是求字符数组的顺序对,因此可以利用 26 大小的哈希数组来求解。 牛牛做了一个 $n*n$ 的棋盘,棋盘每个格子有一个编号(从 1 到 $n * n$),编号是成螺旋形状的,下面给出 $3*3$ 的棋盘编号的例子。 由于隔离在家,牛牛只能自己一个人玩游戏。一开始棋子在 1 号格子上,分数为 0。 严格来说,函数 f(x, y) = 第 x 行第 y 列的格子的编号。比如 n = 3 时,f(2, 2) = 9;n = 4 时,f(3, 1) = 11。 备注: 输入 输出 $∑_{1≤x,y≤n,\\f(x_0,y_0)=i′,\\min(∣x−x_0∣,∣y−y_0∣) 因为查询的次数很多,所以我们希望在常数时间内求解出答案。 如果我们能在常数时间内得到某个矩形内的编号和,那么答案 = 如何在常数时间内得到某个矩形内的编号之和呢?显然我们可以利用前缀和保存一个大矩形的编号和,大矩形 - 小矩形 = 我们要求的矩形。 定义前缀和 时间复杂度:$O(n*n)$。初始化棋盘和前缀和的时间复杂度为 $O(n * n)$, 每一次查询分数的时间复杂度为 $O(1)$。 空间复杂度:$O(n * n)$ 大家好,我是往西汪,一位坚持原创的新人博主。1 ≤ N ≤ 1e5, 1 ≤ k ≤ N
示例1
"cbexa",2
2
cbexa -> cxeba -> excba 步数为2
解法:分组求解
思路分析
顺序对的个数
之和,即可得出答案。代码分析
public int turn (String s, int k) {
int ans = 0;
List
C. 下棋
题目描述
1
2
3
8
9
4
7
6
5
每次牛牛会用骰子掷出 1 - 6 中的一个数字 j (1 ≤ j ≤ 6),然后棋子会沿编号向前移动 j 步(n 号格子后面到 1 号格子),
且分数加上所有行号或者列号与当前格子相差小于 j 的所有格子的编号。
假设当前棋子在 i 号格子上,移动 j 步,那么棋子将移动到 i' 号格子上,其中 i' = (i + j - 1 ) % (n * n) + 1。且分数加上
$∑_{1≤x,y≤n,\\f(x_0,y_0)=i′,\\min(∣x−x_0∣,∣y−y_0∣)对于30%数据,1 ≤ n ≤ 100,1 ≤ q ≤ 1000(q为询问次数)
对于100%数据,1 ≤ n ≤ 2000,1 ≤ q ≤ 100000(q为询问次数)
示例1
4,[1,2,5,6,2]
[48,138,274,410,510]
解法:前缀和
思路分析
红色矩形
+ 绿色矩形
- 蓝色矩形
内编号之和。preSum[i][j]
表示以 (i, j) 为右下角的矩形的编号和。显然前缀和可以通过公式 preSum[i][j] = preSum[i - 1][j] + preSum[i][j - 1] - preSum[i - 1][j - 1] + chessboard[i][j]
来迭代求解。 代码实现
public int[] getScores (int n, int[] query) {
int[] ans = new int[query.length];
int[][] chessboard = new int[n + 1][n + 1]; //棋盘,chessboard[x][y] 表示第 x 行 第 y 列的编号
int[][] coordinates = new int[n * n + 1][2]; //编号对应的下标
long[][] preSum = new long[n + 1][n + 1]; //前缀和,preSum[i][j] 表示以 (i,j) 为右下角的矩形内所有编号之和
init(n, chessboard, coordinates, preSum);
int now = 1;
long score = 0;
for(int i = 0; i < query.length; i++) {
int j = query[i];
now = (now + j - 1) % (n * n) + 1;
int x = coordinates[now][0], y = coordinates[now][1];
score += recSum(n, preSum, 1, y - j + 1, n, y + j - 1) + recSum(n, preSum, x - j + 1, 1, x + j - 1, n) - recSum(n, preSum, x - j + 1, y - j + 1, x + j - 1, y + j - 1);
score %= 1e9 + 7;
ans[i] = (int)score;
}
return ans;
}
//初始化棋盘,编号对应的坐标,前缀和
void init(int n, int[][] chessboard, int[][] coordinates, long[][] preSum){
int[] fx = new int[]{0,1,0,-1};
int[] fy = new int[]{1,0,-1,0};
int x = 1, y = 1, opt = 0;
for(int i = 1; i <= n * n; i++){
chessboard[x][y] = i;
coordinates[i] = new int[]{x, y};
int x1 = x + fx[opt], y1 = y + fy[opt];
if(x1 < 1 || x1 > n || y1 < 1 || y1 > n || chessboard[x1][y1] != 0) opt = (opt + 1) % 4;
x = x + fx[opt];
y = y + fy[opt];
}
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
preSum[i][j] = preSum[i - 1][j] + preSum[i][j - 1] - preSum[i - 1][j - 1] + chessboard[i][j];
}
long recSum(int n, long[][] preSum, int x1, int y1, int x2, int y2) {
if(x1 < 1) x1 = 1;
if(y1 < 1) y1 = 1;
if(x2 > n) x2 = n;
if(y2 > n) y2 = n;
return preSum[x2][y2] - preSum[x1 - 1][y2] - preSum[x2][y1 - 1] + preSum[x1 - 1][y1 - 1];
}
写在最后
如果本文对你有帮助,请动动小手指点个赞。你的支持是我创作路上的最大动力。谢谢大家!
也欢迎来微信公众号【往西汪】找我玩耍~以后会陆续在公众号里更新学习路线、基础知识和技术文章。