题解:洛谷 AT_abc435_b No-Divisible Range

【题目来源】

洛谷:AT_abc435_b [ABC435B] No-Divisible Range - 洛谷

【题目描述】

给定一个长度为 \(N\) 的正整数序列 \(A=(A_1,A_2,\ldots,A_N)\)

请你求出有多少对整数 \((l,r)\) 满足 \(1\leq l\leq r\leq N\),并且满足以下条件:

对于每一个满足 \(l\leq i\leq r\) 的整数 \(i\)\(A_i\) 不是 \(A_l+A_{l+1}+\cdots+A_r\) 的约数。

【输入】

输入从标准输入中读入,格式如下:

\(N\) \(A_1\) \(A_2\) \(\ldots\) \(A_N\)

【输出】

输出满足条件的对数。

【输入样例】

5
8 6 10 5 7

【输出样例】

6

【核心思想】

  1. 问题分析:给定长度为 \(N\) 的正整数序列 \(A\),需要统计满足条件的区间 \([l, r]\) 数量:区间内每个元素 \(A_i\) 都不是区间和 \(\sum_{j=l}^{r} A_j\) 的约数。这是一个暴力枚举 + 前缀和优化问题,关键在于利用前缀和 \(O(1)\) 计算区间和,再 \(O(r-l+1)\) 检查区间内每个元素是否整除该和。

  2. 算法选择

    • 前缀和\(sa[i] = \sum_{j=1}^{i} A_j\),将区间和计算优化至 \(O(1)\)
    • 暴力枚举:由于 \(N \le 50\),直接 \(O(N^2)\) 枚举所有区间,对每个区间 \(O(N)\) 检查整除性,总复杂度 \(O(N^3)\) 完全可接受
  3. 关键步骤

    • 前缀和初始化:计算 \(sa[i] = sa[i-1] + A_i\)\(i\)\(1\)\(N\)
    • 枚举区间\(l\)\(1\)\(N\)\(r\)\(l\)\(N\)):
      • 计算区间和:\(tot = sa[r] - sa[l-1]\)
      • 整除性检查:遍历 \(i\)\(l\)\(r\),若存在 \(tot \% A_i = 0\) 则该区间不合法,跳过
      • 若遍历完均无整除,则 \(ans \mathrel{+}= 1\)
    • 输出 \(ans\)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(N^3)\),枚举 \(O(N^2)\) 个区间,每个区间检查 \(O(N)\) 个元素
    • 空间复杂度:\(O(N)\),原数组 + 前缀和数组
  5. 暴力枚举的核心思想

    • 数据规模决定算法\(N \le 50\)\(N^3 = 125000\),无需复杂优化,直接暴力即可
    • 前缀和降维:将区间求和从 \(O(N)\) 降为 \(O(1)\),是区间问题的基础优化手段
    • 否定即退出:检查函数中一旦发现存在约数立即返回 false,减少无效遍历
    • 适用于"小规模数据 + 区间判定"类问题,核心是准确理解题意并直接翻译为代码

【算法标签】

入门 #模拟

【代码详解】

#include <bits/stdc++.h>
using namespace std;

const int N = 55;  // 最大数组长度
int n;             // 数组长度
int ans;           // 答案:符合条件的区间数量
int a[N];          // 原始数组
int sa[N];         // 前缀和数组,sa[i]=a[1]+a[2]+...+a[i]

/**
 * 检查区间[l,r]的和是否能被区间内任意一个数整除
 * @param res 区间和
 * @param l 区间左端点
 * @param r 区间右端点
 * @return 如果区间和不能被区间内任意数整除,返回true;否则返回false
 */
bool check(int res, int l, int r)
{
    // 遍历区间[l,r]内的每个数
    for (int i = l; i <= r; i++)
    {
        // 如果区间和能被a[i]整除,则不符合条件
        if (res % a[i] == 0)
        {
            return false;
        }
    }
    // 区间和不能被区间内任意数整除
    return true;
}

int main()
{
    // 输入数组长度
    cin >> n;

    // 输入数组并计算前缀和
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
        sa[i] = sa[i - 1] + a[i];  // 前缀和
    }

    // 枚举所有区间[i,j]
    for (int i = 1; i <= n; i++)
    {
        for (int j = i; j <= n; j++)
        {
            // 计算区间[i,j]的和
            int tot = sa[j] - sa[i - 1];

            // 调试输出
            // cout << "tot " << tot << endl;

            // 检查区间和是否能被区间内任意数整除
            if (!check(tot, i, j))
            {
                continue;  // 不符合条件,跳过
            }

            // 调试输出
            // cout << "i j " << i << " " << j << endl;

            // 符合条件,计数加1
            ans++;
        }
    }

    // 输出结果
    cout << ans << endl;

    return 0;
}

【运行结果】

5
8 6 10 5 7
6
posted @ 2026-08-27 11:03  团爸讲算法  阅读(6)  评论(0)    收藏  举报