NOI 模拟赛 小奇的数列Ⅱ ST表+二分

小奇的数列Ⅱ

题目大意:

小奇总在数学课上思考奇怪的问题。
给定⼀个长度为M的数列,小奇定义,若⼀个区间[L,R](1≤L≤R≤n)满⾜:
存在⼀个k(L≤k≤R),使得对于任意的i(L≤i≤R),ai能被ak整除。
称这样的区间为可约的,其价值为R−L
小奇想知道数列中所有可约区间的最⼤价值x,以及价值为x的可约区间个数num,以及它们的左端点。

题目思路:

发现第一个性质:存在一个ak使得区间内 任意的ai都可以被ak整除,那么ak即为该区间的最小的那个数,或者说这个数就是区间的最大公约数。

其次,题目要求最大的长度,那么咱们去二分检测一下就可以了,二分枚举区间长度。判断在当前区间长度下,有没有合法区间,有合法区间就把区间扩大,看是否有更大的,没有就减小,最后二分枚举的答案即为,最长的符合要求的长度。

另外,每一个二分函数都会有一个judge函数,来判断我当前二分的值,合不合法在judge函数中,我们可以判断一下,当前区间的最小值是否可以被这个区间内所有数整除以,之后我们就可以知道存不存在合法区间。关于区间最小值,可以用ST表查询静态区间最小值,因为最小值可以合并所以可以用GCD,并且可以进一步优化,因为gcd也可以区间合并,所以可以进一步预处理出,[s,e]区间内的gcd与最小值,用O(1)的复杂度,去查询就可以把时间复杂度优化到最低,到时判断区间条件就可以改为:区间最小值==区间gcd。

附一下AC代码:

1.只查询最小值,判断区间合法 

#include 
using namespace std;
typedef long long ll;
const ll INF=1000000000000005;
const ll maxn=5e5+5;
const int mod=998244353;
ll n,m;
int num[maxn];
int st[maxn][30];
int lg[maxn];
int save[maxn];
void inint()
{
    scanf("%lld",&n);
    for(int i=1;i<=n;i++) scanf("%lld",&num[i]);
    ll cnt=0;
    for(int i=1;i<=n;i++)
        lg[i]=lg[i/2]+1;
    for(int i=1;i<=n;i++)
        st[i][0]=num[i];
    for(int j=1;j<=25;j++)
        for(int i=1;i+(1<

2. 预处理gcd,判断区间合法

#include 
using namespace std;
typedef long long ll;
const ll INF=1000000000000005;
const ll maxn=5e5+5;
const int mod=998244353;
ll n,m;
int num[maxn];
int st[maxn][30];
int lg[maxn];
int save[maxn];
void inint()
{
    scanf("%lld",&n);
    for(int i=1;i<=n;i++) scanf("%lld",&num[i]);
    ll cnt=0;
    for(int i=1;i<=n;i++)
        lg[i]=lg[i/2]+1;
    for(int i=1;i<=n;i++)
        st[i][0]=num[i];
    for(int j=1;j<=25;j++)
        for(int i=1;i+(1<

可能是由于,预处理GCD的时候,常数太大的问题,第二个比一个慢200ms

 

你可能感兴趣的:(二分,尺取,倍增,st表)