题解:洛谷 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
【核心思想】
-
问题分析:给定长度为 \(N\) 的正整数序列 \(A\),需要统计满足条件的区间 \([l, r]\) 数量:区间内每个元素 \(A_i\) 都不是区间和 \(\sum_{j=l}^{r} A_j\) 的约数。这是一个暴力枚举 + 前缀和优化问题,关键在于利用前缀和 \(O(1)\) 计算区间和,再 \(O(r-l+1)\) 检查区间内每个元素是否整除该和。
-
算法选择:
- 前缀和:\(sa[i] = \sum_{j=1}^{i} A_j\),将区间和计算优化至 \(O(1)\)
- 暴力枚举:由于 \(N \le 50\),直接 \(O(N^2)\) 枚举所有区间,对每个区间 \(O(N)\) 检查整除性,总复杂度 \(O(N^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\)
-
时间/空间复杂度:
- 时间复杂度:\(O(N^3)\),枚举 \(O(N^2)\) 个区间,每个区间检查 \(O(N)\) 个元素
- 空间复杂度:\(O(N)\),原数组 + 前缀和数组
-
暴力枚举的核心思想:
- 数据规模决定算法:\(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
浙公网安备 33010602011771号