题目## 题目
解题思路
这是一个滑动窗口解决组合计数问题。通过维护一个窗口来找到所有满足距离要求的建筑物组合。
关键点:
- 使用滑动窗口
- 计算组合数
- 处理大数运算
- 结果取模
算法步骤:
- 维护窗口左右边界
- 根据距离条件调整窗口
- 计算当前窗口内的组合数
- 累加结果并取模
代码
#include <bits/stdc++.h>
using namespace std;
class BuildingSelector {
private:
const int MOD = 99997867;
// 计算在范围内选择2个点的组合数
long long calculateCombinations(int range) {
if (range < 2) return 0;
long long n = range;
return (n * (n - 1)) / 2;
}
public:
int findValidCombinations(vector<int>& buildings, int maxDistance) {
long long totalCombinations = 0;
int leftPtr = 0;
int n = buildings.size();
// 使用双指针遍历所有可能的组合
for (int rightPtr = 0; rightPtr < n; rightPtr++) {
// 当距离超过限制时,移动左指针
while (leftPtr < rightPtr &&
buildings[rightPtr] - buildings[leftPtr] > maxDistance) {
leftPtr++;
}
// 计算当前窗口内的有效组合数
if (rightPtr - leftPtr >= 2) {
totalCombinations = (totalCombinations +
calculateCombinations(rightPtr - leftPtr)) % MOD;
}
}
return static_cast<int>(totalCombinations);
}
};
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int buildingCount, maxDistance;
cin >> buildingCount >> maxDistance;
vector<int> buildingPositions(buildingCount);
for (int& pos : buildingPositions) {
cin >> pos;
}
BuildingSelector selector;
cout << selector.findValidCombinations(buildingPositions, maxDistance) << endl;
return 0;
}
import java.util.*;
class BuildingSelector {
private static final int MODULO = 99997867;
// 计算有效范围内的组合数
private long getValidCombinations(int range) {
if (range < 2) return 0;
long n = range;
return (n * (n - 1)) / 2;
}
public int findPossiblePlans(int[] buildings, int maxDist) {
long totalPlans = 0;
int windowStart = 0;
for (int windowEnd = 0; windowEnd < buildings.length; windowEnd++) {
// 调整窗口起始位置
while (windowStart < windowEnd &&
buildings[windowEnd] - buildings[windowStart] > maxDist) {
windowStart++;
}
// 计算当前窗口内的可能方案数
int windowSize = windowEnd - windowStart;
if (windowSize >= 2) {
totalPlans = (totalPlans + getValidCombinations(windowSize)) % MODULO;
}
}
return (int) totalPlans;
}
}
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int d = scanner.nextInt();
int[] buildingPositions = new int[n];
for (int i = 0; i < n; i++) {
buildingPositions[i] = scanner.nextInt();
}
BuildingSelector selector = new BuildingSelector();
System.out.println(selector.findPossiblePlans(buildingPositions, d));
scanner.close();
}
}
class BuildingSelector:
def __init__(self):
self.MODULO = 99997867
def _calculate_combinations(self, window_size: int) -> int:
"""计算窗口内可能的组合数"""
if window_size < 2:
return 0
return (window_size * (window_size - 1)) // 2
def find_valid_plans(self, buildings: list, max_distance: int) -> int:
"""查找所有有效的埋伏方案数"""
total_plans = 0
left = 0
n = len(buildings)
# 使用滑动窗口查找有效组合
for right in range(n):
# 调整窗口左边界,确保最大距离不超过限制
while left < right and buildings[right] - buildings[left] > max_distance:
left += 1
# 计算当前窗口内的有效方案数
window_size = right - left
if window_size >= 2:
total_plans = (total_plans + self._calculate_combinations(window_size)) % self.MODULO
return total_plans
def main():
# 读取输入
n, d = map(int, input().split())
building_positions = list(map(int, input().split()))
# 计算结果
selector = BuildingSelector()
result = selector.find_valid_plans(building_positions, d)
print(result)
if __name__ == "__main__":
main()
算法及复杂度
- 算法:滑动窗口
- 时间复杂度: O ( N ) \mathcal{O(N)} O(N),其中 N N N是建筑物数量
- 空间复杂度: O ( 1 ) \mathcal{O(1)} O(1),只需要常数空间
题解
题目难度:较难
知识点:位运算
解题思路:在思考这道题是,首先想到的可能是按照数据的顺序,一个一个数比较,标记出只出现一次的数,输出这个数即可,但是这个过程的复杂度很高,复杂度为n^2。
在考虑这道题时,我们可以先思考一个简单版本:一个数组只有一个数字只出现一次,其他的数字都出现了两次。怎么查出这个数?
这个题目强调有一个数字出现一次,其他的出现两次。所以我们想到了异或运算的性质:任何一个数字异或它自己都等于0。所以我们只需要从头到尾异或所有的数,这是整个过程中,两个相同的数据就会全部抵消掉了,就只会剩下那个不同的数据。所以,一个数据组中如果只有一个不同,异或整个数组后就可以找出它。所以,对于这到原题,我们需要考虑的就是把这整个数据组分为两个数组,每个数组只包括一个不同的数据,单独异或这两个数据就可以了。
有了这个思路后,我们还是从头到尾依次异或数组中的每一个数字,那么最终得到的结果就是两个只出现一次的数字的异或结果。因为这个数肯定不为0,找出它的1的位置,标记这个位置,然后按照这个完整的数据中的数据,在这个标记的位置是否为1,就可以分为两个数据组,每个数据组只含有一个不同的数据,然后就可以找出这两个数字了。
解题
这是一道考察位运算的题。两个相同的数进行异或其结果为0。所以当所有值进行了一次异或后,最后的结果就是这两个不同值的异或结果。然后通过与运算和位移运算,找到异或后结果为1的位置,此位置所有为0的进行异或和所有为1的异或就是结果。
#include <bits stdc++.h>
using namespace std;
int a[1000001];
void getNumber(const int a[], int n, int&num1, int&num2)
{
int OR= 0;
int flag = 0;
for(int i = 1; i <= n; i++)
{
OR ^= a[i];
}
while((OR & 1) == 0)
{
OR = OR >> 1;
flag++;
}
for(int i = 1; i <= n; i++)
{
if((a[i] >> flag) & 1)
num1 ^= a[i];
else
num2 ^= a[i];
}
}
int main(void)
{
int n = 0;
while (~scanf("%d", &a[n + 1])) ++n;
int p, q;
getNumber(a, n, p, q);
if (p > q) swap(p, q);
printf("%d %d\n", p, q);
return 0;
}

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



