2021年9月海淀区赛小学组 区间划分 题解

 题目描述

N 个数组组成序列 A,求该序列最少可以划分成多少个区间,某个 \{i,j\} 区间中的数 A_i 到 A_j从小到大排序后,一定是公差大于 1等差数列子序列

 输入格式

第一行一个正整数 N
接下来一行包含 N 个正整数,第i个正整数为 A_i

 输出格式

仅有一个正整数,表示最少可以被划分的区间数。

 样例

样例输入 #1

7
1 5 11 2 6 4 7

样例输出 #1

3

样例输入 #2

8
4 2 6 8 5 3 1 7

样例输出 #2

2

 提示

对于 20\% 的数据满足,N \le 10
40\% 的数据满足,N \le 100
60\% 的数据满足,N \le 10001 \le Ai \le10^6
另有 20\% 的数据满足,A_i 互不相同。
100\% 的数据满足,N \le 1000001 \le Ai \le 10^9

题目传送门(题目源自洛谷)

这道题主要思路是贪心,也没想的那么难,我写的 24ms,感觉够快了。

 思路

若干个数,如果可以属于公差大于 1 的等差数列的子序列。那么他们首先不能有相同的数,其次两两之间的差的最大公因数一定大于 1

我们利用贪心,把每个数尽可能地向前一段分,直到出现相同的数,或者两项之间的公差的最大公因数等于 1 为止,则需要另起一段新的划分,此时计数器加 1

由于 $1 \le Ai \le10^9$ ,我们就用 $map$ 记录数字是否重复出现,时间复杂度为 $O(nlog(n))$

有个坑点就是:判断公差为 1 的时候如果写 if(abs(a[i]-a[i+1])==1) 只能拿 86 分,切记写上 if(abs(a[i]-a[i+1])<=1)


 完整代码

#include<bits/stdc++.h>
using namespace std;
map<int,int> so;
int a[100005];
int gcd(int a,int b){//二进制 Gcd
    if(a==0) return b;
    if(b==0) return a;
    if(!(a&1)&&!(b&1)) return 2*gcd(a>>1,b>>1);
    else if(!(a&1)) return gcd(a>>1,b);
    else if(!(b&1)) return gcd(a,b>>1);
    else return gcd(abs(a-b), min(a,b));
}
int main()
{
    int n,cnt=0;
    register int i,j;
    scanf("%f",&n);
    for(i=1;i<=n;i++) scanf("%d",&a[i]);
    for(i=1;i<=n;i++){
        so[a[i]]++;
        //so记录每个数的出现次数
        if(abs(a[i]-a[i+1])<=1){
            //abs函数取绝对值,因为数组不是从小到大排序
            //如果公差为 1就另起一段区间
            cnt++;
            so.clear();
             continue;
        }
        i++,so[a[i]]++;
        int d=abs(a[i]-a[i-1]);
        for(j=i+1;j<=n;j++){
            d=gcd(abs(a[j]-a[j-1]),d);
            if(d==1||so[a[j]]>0) break;
            //如果两两之间的公差为 1
            //或是一个数多次出现
            so[a[j]]++;
        }
        cnt++,i=j-1;
        //i直接跳到下一段开始的位置
        //j减去一是因为下一次循环开始时 i会加 1
        so.clear();
        //每次循环结束别忘了清空 map
    }
    printf("%d",cnt);
    return 0;
}

防抄袭万岁

 拓展知识点

 二进制Gcd

二进制 $Gcd$ 只使用位移加减法实现,因此运行效率较高,尤其在求大数的 $Gcd$ 时,效率明显提高,这是因为大数除法的实现很复杂。(就比如说本题的 $Ai$

二进制 Gcd 计算当中共有 4 种情况:

        1.   $a,b$ 为偶数,则 $Gcd(a,b)=2*Gcd(a/2,b/2)$

        2.   $a,b$ 为奇数,则 $Gcd(a,b)=Gcd((a-b)/2,b)$

        3.   a 为奇数,b 为偶数,则 $Gcd(a,b)=Gcd(a,b/2)$

        4.   a0,则返回 b

 优雅の代码

typedef unsigned long long ll;
ll gcd(ll a, ll b) {
    if (a == 0)
        return b;
    if (b == 0)
        return a;
    if (!(a & 1) && !(b & 1))
        return 2 * gcd(a >> 1, b >> 1);
    else if (!(a & 1))
        return gcd(a >> 1, b);
    else if (!(b & 1))
        return gcd(a, b >> 1);
    else
        return gcd(abs(a - b), min(a, b));
}
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值