![](https://img2024.cnblogs.com/blog/3375279/202410/3375279-20241029201209127-795198216.png)
点击查看代码
/* 台州第一深情 */
#include <bits/stdc++.h>
using namespace std;
using i64 = long;
using ll = long long;
typedef pair<int, int> PII;
const int N = 1e5 + 5;
int n, t;
int a[N], max1[N][25], min1[N][25]; // max1[i][j]表示以i结尾,长度为2^j的子序列的最大值,min1[i][j]表示以i结尾,长度为2^j的子序列的最小值
int mlog[N]; // 代表2的多少次方能刚好小于等于n
void init()
{
mlog[0] = -1;
for (int i = 1; i <= N; ++i)
{
mlog[i] = mlog[i >> 1] + 1; // 将mlog[0]设为1,这样后面的都可以从前面得出,例i=1,1>>2=0,mlog[1]=0,i=2,2>>2=1,mlog[2]=1,i=4,4>>2=2,mlog[4]=2,i=8,8>>2=3,mlog[8]=3,如此
}
for (int i = 1; i <= n; i++)
{
max1[i][0] = min1[i][0] = a[i]; // 初始化,再下标为i长度为2^0时为a[i];
}
int p = mlog[n];
for (int k = 1; k <= p; ++k) // 枚举以i为起点的长度
{
for (int s = 1; s + (1 << k) - 1 <= n; s++) // s为下标,下标加长度不能越界
{
max1[s][k] = max(max1[s][k - 1], max1[s + (1 << (k - 1))][k - 1]); // 这个代码的原理就是因为k为2的k次方,所以我们可以将其一分为2,然后比较两个区间的最值例如已经当k为0的时候,每个max1[i][0]都为其本身,当k为1时,max1[i][1]长度为2,那第一个区间的初始位置就是i,而另一个区间的初始位置就是i加上一般的长度,也就是i+1<<(k-1),而数组的长度也自然就是当前需要长度的一半,因为k是2的k次方,所以2的k次方一般也就是2的(k-1)次方
min1[s][k] = min(min1[s][k - 1], min1[s + (1 << (k - 1))][k - 1]);
}
}
}
int solve(int l, int r)
{
int k = mlog[r - l + 1]; // k代表的是2的多少次方能刚好小于等于r-l+1
int l1 = r - (1 << k) + 1; // 如上面初始化解释
int max2 = max(max1[l][k], max1[l1][k]);
int min2 = min(min1[l][k], min1[l1][k]);
return max2 - min2;
}
int main()
{
cin >> n >> t;
for (int i = 1; i <= n; ++i)
{
cin >> a[i];
}
init();
while (t--)
{
int l, r;
cin >> l >> r;
cout << solve(l, r) << "\n";
}
return 0;
}
![](https://img2024.cnblogs.com/blog/3375279/202410/3375279-20241029205655855-133299079.png)
点击查看代码
/* 台州第一深情 */
#include <bits/stdc++.h>
using namespace std;
using i64 = long;
using ll = long long;
typedef pair<int, int> PII;
const int N = 1e5 + 5;
int n, t;
int a[N], max1[N][25], min1[N][25];
int mlog[N];
void init()
{
mlog[0] = -1;
for (int i = 1; i <= N; ++i)
{
mlog[i] = mlog[i >> 1] + 1;
}
for (int i = 1; i <= n; i++)
{
max1[i][0] = min1[i][0] = a[i];
}
int p = mlog[n];
for (int k = 1; k <= p; ++k) // 2的k次方
{
for (int s = 1; s + (1 << k) <= n + 1; s++) // 下标
{
max1[s][k] = __gcd(max1[s][k - 1], max1[s + (1 << (k - 1))][k - 1]);
min1[s][k] = min(min1[s][k - 1], min1[s + (1 << (k - 1))][k - 1]);
}
}
}
int solve(int l, int r)
{
int k = mlog[r - l + 1];
int l1 = r - (1 << k) + 1;
int max2 = __gcd(max1[l][k], max1[l1][k]);
int min2 = min(min1[l][k], min1[l1][k]);
return max2;
}
int main()
{
cin >> n >> t;
for (int i = 1; i <= n; ++i)
{
cin >> a[i];
}
init();
while (t--)
{
int l, r;
cin >> l >> r;
cout << solve(l, r) << "\n";
}
return 0;
}
这道题的其他地方与第一题一样,但为什么可以用gcd呢,因为里面的元素是可以重复运算的,被举个例子,3,6,12,24,假设我们要求1-3的区间的最大公约数,首先,gcd[i][0]都是其本身,而gcd[1][1]为3跟6比较,而gcd[2][1]为6跟12比较,此时,6比较了两次,但是3跟6的最大公约数与6跟12的最大公约数其实就相当于里面三个数的最大公约数。加入把3改成2,gcd(2,3)=1,gcd(3,6)=3,而gcd(1,3)=1答案还是正确的。所以st算这题是没问题的