B. Integers Have Friends(cf)二分 + st表

 原题链接:Problem - 1548B - Codeforces

解决RMQ(区间最值)问题的算法。

总结的来说求rmq问题有多种方法:线段树,ST表等 线段树预处理O(nlogn),查询O(logn),支持在线修改 ST表预处理O(nlogn),查询O(1),但不支持在线修改

先用st表预处理,然后再使用二分求出最大的区间长度,每次查询一个区间的时候用st表十分方便。

我的二分在这里wa了很多发,注意如果n == 2的时候这个二分是进不了while循环的,所以我单独拿出来处理了;然后这个二分还要想清楚就是,如果在这个区间里没有答案,它就会以最左端点为答案,所以最后还要把得出的l特判一下---->或者,我刚才去试了一下,直接把左端点改为0就万事大吉了(之前是怕左端点为0会出啥问题之类的)。

AC代码:

#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
typedef pair<int, int> PII;
const double pi = acos(-1.0);
#define rep(i, n) for (int i = 1; i <= (n); ++i)
#define rrep(i, n) for (int i = n; i >= (1); --i)
typedef long long ll;
#define sqar(x) ((x)*(x))

const int N = 2e5 + 10;
ll a[N];
int Log[N];
ll dp[N][21]; //以下标i为起点,区间长度2^j的区间维护的值

ll gcd(ll a, ll b)
{
    return b == 0 ? a : gcd(b, a % b);
}

int n;
void init(){
for (int i = 2; i < N; i++)
    Log[i] = Log[i >> 1] + 1;
}

void st() {
    for (int i = 1; i < n; i++)
        dp[i][0] = a[i];
    for (int j = 1; j <= 20; j++){
        for (int i = 1; i + (1 << j) - 1 <= n - 1; i++) {
            dp[i][j] = gcd(dp[i][j - 1], dp[i + (1 << (j - 1))][j - 1]);
        }
    }
}

ll query(int l, int r) {
    int k = Log[r - l + 1];
    return gcd(dp[l][k], dp[r - (1 << k) + 1][k]);
}

bool check(int x)
{
    for(int i = x; i < n; i++)
    {
        if(query(i - x + 1, i) > 1ll) return 1;
    }
    return 0;
}

int main() {
    int t;
    scanf("%d", &t);
    init();
    while(t--){
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) scanf("%lld", &a[i]);
    if(n == 2){ //n等于2特判一下,因为后面写二分的时候如果n变成1了没进while
        if(abs(a[2] - a[1]) == 1) puts("1");
        else puts("2");
        continue;
    }
    rep(i, n - 1)
    {
        a[i] = abs(a[i + 1] - a[i]);
    }
    st();
    int l = 1, r = n - 1;
    int ans = 0;
    while(l < r)
    {
        int mid = (l + r + 1) >> 1;
        if(check(mid)) l = mid;
        else r = mid - 1;
    }
    ans = l;
    if(!check(l)) ans = 0;
    printf("%d\n", ans + 1);
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值