1503. Integer Inquiry

本文介绍了一个简单的程序设计问题——如何在一个超级计算机上处理并计算多个非常大的整数的总和。通过逆序读取每个大数的每一位,并进行逐位相加的方式处理进位,最终实现了大数求和的功能。

Description

One of the first users of BIT’s new supercomputer was Chip Diller. He extended his exploration of powers of 3 to go from 0 to 333 and he explored taking various sums of those numbers.
This supercomputer is great,'' remarked Chip.I only wish Timothy were here to see these results.” (Chip moved to a new apartment, once one became available on the third floor of the Lemon Sky apartments on Third Street.)
Input

The input will consist of at most 100 lines of text, each of which contains a single VeryLongInteger. Each VeryLongInteger will be 100 or fewer characters in length, and will only contain digits (no VeryLongInteger will be negative).

The final input line will contain a single zero on a line by itself.
Output

Your program should output the sum of the VeryLongIntegers given in the input.
Sample Input

123456789012345678901234567890
123456789012345678901234567890
123456789012345678901234567890
0
Sample Output

370370367037037036703703703670


思路

这道题比较简单,单独考虑每一位的相加,处理一下进位就ok了!
注意:

  1. 求和的sum数组大小至少sum[102];
  2. 题目中有no VeryLongInteger will be negative 这样的描述,所以输入中可能有以0开头的数,判断输入结束应该使用strcmp(a,”0”)语句,而非a[0]!=0;

代码

#include <iostream>
#include <string.h>

using namespace std;

int main()
{
    char a[101];
    int sum[102] = { 0 };
    int i = 0,j = 0;

    cin >> a;
    while (strcmp(a,"0"))
    {
        for (i = 0; i < strlen(a); i++)
        {
            sum[i] += a[strlen(a) - i - 1] - '0';
            if (sum[i] >= 10)   //进位的处理
            {
                sum[i] -= 10;
                sum[i + 1] += 1;
                j = i + 1;
            }
            else {
                j = i;
            }   
        }
        cin >> a;
    }

    for (i = j; i >= 0; i--)  //将sum逆序输出
    {
        cout << sum[i];
    }

    return 0;
}
这是一个 **多次区间按位与(AND)查询** 的问。 --- ## ✅ 问重述 - 多组输入 - 每组: - 第一行:`n`, `m` → 数列长度和查询次数(`n < 100000`, `m < 5000`) - 第二行:`n` 个正整数 $ A_1, A_2, ..., A_n $,每个 $ A_i < 2^{63} $ - 接下来 `m` 行,每行两个整数 `i j`,表示查询区间 $[i, j]$ 的 **按位与结果**: $$ A_i \& A_{i+1} \& \cdots \& A_j $$ > 注意:目中下标是从 1 开始的! 输出每个查询的结果,并且 **不同测试数据之间输出一个空行** --- ### 📌 样例解析 输入: ``` 4 2 1 3 2 6 1 2 2 4 ``` 数组为:`[1, 3, 2, 6]`(索引从 1 开始) #### 查询 1: `1 2` → $ A_1 \& A_2 = 1 \& 3 $ - `1` 的二进制:`01` - `3` 的二进制:`11` - `1 & 3 = 1` ✅ 输出:`1` #### 查询 2: `2 4` → $ A_2 \& A_3 \& A_4 = 3 \& 2 \& 6 $ - `3`: `011` - `2`: `010` - `6`: `110` - `3 & 2 = 010 = 2` - `2 & 6 = 010 & 110 = 010 = 2` ✅ 输出:`2` 最终输出: ``` 1 2 ``` 并且如果有多个测试用例,中间加一个空行。 --- ## ❗ 关键挑战 - `n` 最大为 100000,`m` 最大为 5000 - 如果对每个查询都暴力遍历区间做 `&` 运算,最坏时间复杂度是 $ O(m \cdot n) = 5e9 $,可能超时 - 而且数值很大($ < 2^{63} $),不能使用普通前缀和(因为 `&` 不可逆) --- ## ✅ 解法选择 虽然 **按位与不满足可减性**,但我们可以利用其两个重要性质: ### ✅ 性质 1:单调性(非增) 对于固定起点 i,随着右端点 j 增大,区间与值: $$ x = A_i \& A_{i+1} \& \cdots \& A_j $$ 只会 **变小或不变**。因为每次 `&` 一个数,某些位可能被清零,无法恢复。 ### ✅ 性质 2:最多变化 $ O(\log \max A) $ 次 因为在 64 位整数范围内,每一位只能从 1 变成 0 一次,所以对于任意起始点 i,随着 j 扩展,区间与值最多改变 64 次。 这启发我们使用 **稀疏表(Sparse Table)** 或 **离线查询 + 分治优化**,但考虑到 `m` 只有 5000,而 `n` 是 1e5,我们可以采用以下几种方案: --- ## ✅ 推荐解法:**分块预处理 + 位优化剪枝暴力** 但由于 `m=5000` 不算太大,且按位与具有强剪枝特性,可以尝试 **简单优化的暴力查询**: > 对每个查询 `[l, r]`,从 `l` 到 `r` 遍历,同时如果当前结果变为 0,则提前终止(因为 `0 & anything = 0`) 这个剪枝在很多情况下非常高效。 --- ## ✅ 更优解法:**稀疏表(Sparse Table)支持区间与查询** 由于 `&` 是幂等、结合、交换的,且满足区间合并性质,我们可以用 **RMQ 类似结构** 构造 Sparse Table 来实现 $ O(1) $ 查询。 但注意:**Sparse Table 通常用于最小值、GCD、AND、OR 等满足幂等性和结合律的操作** ✅ 按位与满足: - $ a \& a = a $(幂等) - 支持 ST 表构建 --- ## ✅ 使用 Sparse Table 实现 $ O(n \log n) $ 预处理,$ O(1) $ 查询 --- ### 🔧 构建思路 设 `st[j][i]` 表示从位置 `i` 开始,长度为 $ 2^j $ 的区间的 AND 值 递推式: ```text st[0][i] = A[i] st[j][i] = st[j-1][i] & st[j-1][i + (1 << (j-1))] ``` 查询区间 `[l, r]`(0-indexed): - 找最大 `k` 使得 $ 2^k \leq r-l+1 $ - 结果 = `st[k][l] & st[k][r - (1<<k) + 1]` --- ## ✅ C++ 实现(Sparse Table + 多组数据 + 格式控制) ```cpp #include <iostream> #include <vector> #include <cmath> #include <algorithm> using namespace std; typedef long long ll; const int MAXN = 100000; const int LOG = 64; // 因为数字到 2^63,但数组长度 log2(1e5)≈17 ll st[LOG][MAXN]; int len_log[MAXN + 1]; // 预处理 log2 值 // 预处理 log2 表 void init_logs() { len_log[1] = 0; for (int i = 2; i <= MAXN; ++i) { len_log[i] = len_log[i / 2] + 1; } } // 构建 Sparse Table void build_st(vector<ll>& arr, int n) { for (int i = 0; i < n; ++i) { st[0][i] = arr[i]; } for (int j = 1; (1 << j) <= n; ++j) { for (int i = 0; i + (1 << j) <= n; ++i) { st[j][i] = st[j-1][i] & st[j-1][i + (1 << (j-1))]; } } } // 查询 [l, r] 区间 (inclusive), 0-indexed ll query_and(int l, int r) { int length = r - l + 1; int k = len_log[length]; return st[k][l] & st[k][r - (1 << k) + 1]; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); init_logs(); bool first_case = true; int n, m; while (cin >> n >> m) { if (!first_case) { cout << '\n'; // 每两组之间输出一个空行 } first_case = false; vector<ll> A(n); for (int i = 0; i < n; ++i) { cin >> A[i]; } build_st(A, n); for (int q = 0; q < m; ++q) { int i, j; cin >> i >> j; // 转换为 0-indexed int l = i - 1, r = j - 1; ll res = query_and(l, r); cout << res << '\n'; } } return 0; } ``` --- ## ✅ 时间复杂度分析 - 预处理:$ O(n \log n) $ - 单次查询:$ O(1) $ - 总体:$ O(n \log n + m) $,适合大 `n` 和 `m` --- ## ✅ 替代方案:暴力 + 剪枝(适用于小范围或随机数据) ```cpp // 对于每个查询暴力计算,带剪枝 ll query_brute(const vector<ll>& A, int l, int r) { ll res = A[l]; for (int i = l + 1; i <= r && res != 0; ++i) { res &= A[i]; } return res; } ``` 在某些情况下(如很快归零),性能接近线性。 但对于最坏情况(始终非零),仍是 $ O(n m) = 5e9 $,不可接受。 因此推荐 **Sparse Table**。 --- ## ✅ 注意事项 - 下标从 1 开始 → 记得减 1 - 多组输入 → 使用 `while(cin >> n >> m)` - 组间输出一个空行 → 控制第一个不输出前导空行 - 使用 `long long` 存储数据(`< 2^63`) ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值