题目## 题目## 题目
解题思路
这是一个环形手串颜色检查问题。需要检查每种颜色在任意连续 m m m 个串珠中是否出现超过一次。
关键点:
- 处理环形结构
- 记录每种颜色的出现位置
- 检查连续 m m m 个串珠的颜色分布
- 考虑无色串珠的特殊情况
算法步骤:
- 记录每种颜色的出现位置
- 对每种颜色检查是否符合要求
- 统计不符合要求的颜色数量
- 考虑环形结构的边界情况
代码
#include <bits/stdc++.h>
using namespace std;
class Solution {
public:
int solve(int n, int m, int c, vector<vector<int>>& beads) {
// 记录每种颜色的出现位置
vector<vector<int>> colorPos(c + 1);
for (int i = 0; i < n; i++) {
for (int color : beads[i]) {
colorPos[color].push_back(i);
}
}
int invalid = 0;
// 检查每种颜色
for (int color = 1; color <= c; color++) {
if (colorPos[color].empty()) continue;
// 检查是否在连续m个串珠中出现超过一次
for (int i = 0; i < colorPos[color].size(); i++) {
for (int j = i + 1; j < colorPos[color].size(); j++) {
int dist = colorPos[color][j] - colorPos[color][i];
if (dist < m || (n - dist) < m) {
invalid++;
i = colorPos[color].size(); // 跳出外层循环
break;
}
}
}
}
return invalid;
}
};
int main() {
int n, m, c;
cin >> n >> m >> c;
vector<vector<int>> beads(n);
for (int i = 0; i < n; i++) {
int num;
cin >> num;
beads[i].resize(num);
for (int j = 0; j < num; j++) {
cin >> beads[i][j];
}
}
Solution solution;
cout << solution.solve(n, m, c, beads) << endl;
return 0;
}
import java.util.*;
public class Main {
static class Solution {
public int solve(int n, int m, int c, List<List<Integer>> beads) {
// 记录每种颜色的出现位置
List<List<Integer>> colorPos = new ArrayList<>(c + 1);
for (int i = 0; i <= c; i++) {
colorPos.add(new ArrayList<>());
}
for (int i = 0; i < n; i++) {
for (int color : beads.get(i)) {
colorPos.get(color).add(i);
}
}
int invalid = 0;
// 检查每种颜色
for (int color = 1; color <= c; color++) {
if (colorPos.get(color).isEmpty()) continue;
List<Integer> positions = colorPos.get(color);
// 检查是否在连续m个串珠中出现超过一次
outer:
for (int i = 0; i < positions.size(); i++) {
for (int j = i + 1; j < positions.size(); j++) {
int dist = positions.get(j) - positions.get(i);
if (dist < m || (n - dist) < m) {
invalid++;
break outer;
}
}
}
}
return invalid;
}
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int m = sc.nextInt();
int c = sc.nextInt();
List<List<Integer>> beads = new ArrayList<>(n);
for (int i = 0; i < n; i++) {
int num = sc.nextInt();
List<Integer> colors = new ArrayList<>();
for (int j = 0; j < num; j++) {
colors.add(sc.nextInt());
}
beads.add(colors);
}
Solution solution = new Solution();
System.out.println(solution.solve(n, m, c, beads));
sc.close();
}
}
class Solution:
def solve(self, n, m, c, beads):
# 记录每种颜色的出现位置
color_pos = [[] for _ in range(c + 1)]
for i in range(n):
for color in beads[i]:
color_pos[color].append(i)
invalid = 0
# 检查每种颜色
for color in range(1, c + 1):
if not color_pos[color]:
continue
# 检查是否在连续m个串珠中出现超过一次
for i in range(len(color_pos[color])):
for j in range(i + 1, len(color_pos[color])):
dist = color_pos[color][j] - color_pos[color][i]
if dist < m or (n - dist) < m:
invalid += 1
i = len(color_pos[color]) # 跳出外层循环
break
if i == len(color_pos[color]):
break
return invalid
# 读取输入
n, m, c = map(int, input().split())
beads = []
for _ in range(n):
nums = list(map(int, input().split()))
beads.append(nums[1:] if nums[0] > 0 else [])
solution = Solution()
print(solution.solve(n, m, c, beads))
算法及复杂度
- 算法:模拟
- 时间复杂度: O ( c ⋅ k 2 ) \mathcal{O(c \cdot k^2)} O(c⋅k2),其中 k k k 是每种颜色平均出现的次数
- 空间复杂度: O ( n ) \mathcal{O(n)} O(n),用于存储颜色位置信息
解题思路
这是一道计算两个整数二进制表示中不同位数的题目,主要思路如下:
-
问题分析:
- 给定两个int32整数 m m m 和 n n n
- 需要计算它们的二进制表示中有多少位不同
- 例如:3(11)和5(101)有2位不同
-
解决方案:
- 使用异或运算(^)找出不同的位(结果为1的位就是不同的位)
- 使用Brian Kernighan算法计算1的个数
- r & ( r − 1 ) r \& (r-1) r&(r−1) 可以消除最右边的1
-
优化技巧:
- 不需要实际转换成二进制字符串
- 使用位运算可以高效计算
代码
class Solution {
public:
int countBitDiff(int m, int n) {
// 异或运算找出不同的位
int r = m ^ n;
int count = 0;
// Brian Kernighan算法计算1的个数
while(r) {
r &= (r - 1); // 消除最右边的1
count++;
}
return count;
}
};
public class Solution {
public int countBitDiff(int m, int n) {
// 异或运算找出不同的位
int r = m ^ n;
int count = 0;
// Brian Kernighan算法计算1的个数
while(r != 0) {
r &= (r - 1); // 消除最右边的1
count++;
}
return count;
}
}
class Solution:
def countBitDiff(self, m: int, n: int) -> int:
# 异或运算找出不同的位
r = m ^ n
count = 0
# Brian Kernighan算法计算1的个数
while r:
r &= (r - 1) # 消除最右边的1
count += 1
return count
算法及复杂度
- 算法:Brian Kernighan算法
- 时间复杂度: O ( k ) \mathcal{O}(k) O(k) - k k k 为二进制中1的个数
- 空间复杂度: O ( 1 ) \mathcal{O}(1) O(1) - 只需要常数级别的额外空间
题解:
考察点: 数学,打表找规律
易错点:
注意最好把 a a a和 b b b都开成long long类型,因为在计算的过程中有可能会爆 i n t int int
解法:打表找规律
这题第一眼看上去并没有什么神奇的数学结论可以一眼秒掉,但数据范围又这门大,很显然可以通过打表来找规律。于是对 100 100 100以内的小数据进行暴力
#include "bits/stdc++.h"
using namespace std;
int main()
{
for(int i=1;i<=100;i++){
int sum=0;
for(int j=1;j<=i;j++){
sum+=i/j;
}
if(sum%2==0){
if(i%100==0) printf("%d\n",i);
else printf("%d ",i);
}
}
printf("\n");
return 0;
}
整理结果后有如下结果
4 5 6 7 8
16 17 18 19 20 21 22 23 24
36 37 38 39 40 41 42 43 44 45 46 47 48
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
100
很显然可以从上表中观察出来,当 i i i为偶数时,位于区间 [ i 2 , ( i + 1 ) 2 ) [i^2,(i+1)^2) [i2,(i+1)2)内数全为满足要求的怪数。
有了这个结论之后,对于整数 X X X,就可以快速统计出 [ 1 , X ] [1,X] [1,X]内的怪数个数。设 v = [ X ] v=[\sqrt{X}] v=[X],则对 v v v进行分类讨论。如果 v v v是奇数,则说明从 v 2 v^2 v2一直到 X X X之后不可能再存在怪数。而前面的怪数个数 ∑ i = 0 i = v − 1 2 4 × i + 1 \sum_{i=0}^{i=\frac{v-1}2}4\times i+1 ∑i=0i=2v−14×i+1。而如果 v v v是偶数,则说明后面从 v 2 v^2 v2到 X X X还存在怪数,故结果应该是 ∑ i = 0 i = v − 1 2 − 1 4 × i + 1 \sum_{i=0}^{i=\frac{v-1}2- 1}4\times i+1 ∑i=0i=2v−1−14×i+1最后还应该加上 X − v 2 + 1 X-v^2+1 X−v2+1。最后的集过应该为 [ 1 , b ] [1,b] [1,b]区间内的个数,减去 [ 1 , a − 1 ] [1,a-1] [1,a−1]区间内的个数。
#include "bits/stdc++.h"
using namespace std;
typedef long long LL;
LL a,b;
LL solve(LL x){
if(x<0) return 0;
if(x==0) return 1;
LL v=sqrt(x),s=0;
if(v%2){
return v*(v+1)/2;
}else{
LL t=v-1;
s=t*(t+1)/2;
s+=x-v*v+1;
}
return s;
}
int main()
{
scanf("%lld%lld",&a,&b);
printf("%lld\n",solve(b)-solve(a-1));
return 0;
}

被折叠的 条评论
为什么被折叠?



