Week 1-快速幂,二分查找和二分答案

该篇博客,以及后面的标题类似的博客用于记录洛谷的作业。

 贴一下一个同学分享的博客,就是看这个学的二分:二分查找 & 二分答案 万字详解,超多例题,带你学透二分。_二分法例题及其答案_小酒窝.的博客-CSDN博客

1.P8682 等差数列

#include<iostream>
#include<algorithm>
using namespace std;

int gcd(int a, int b)
{
    if (a%b==0) return b;
    else return gcd(b, a % b);
}

int main()
{
    int n; cin>>n;
    int arr[100005];
    for(int i=0;i<n;i++) cin>>arr[i];
    sort(arr,arr+n);
    if(arr[0]==arr[n-1]) {cout<<n; return 0;}

    int d=arr[1]-arr[0];
    for(int i=2;i<n;i++) d=gcd(d,arr[i]-arr[i-1]);
    cout<<(arr[n-1]-arr[0])/d+1;
    return 0;
}

思路:先排序,然后在各项之差中用循环找最大公约数,最大公约数用辗转相除法求得。最后用数列中最大项与最小项之差除以最大公差加一即为项数。

2.P1226 快速幂

#include<iostream>
using namespace std;
long long quickpower(long long a,long long b,long long c){
    long long ans=1;
    while(b){
        if(b&1){
            ans=(ans*a)%c;
        }
        a=(a*a)%c;
        b/=2;
    }
    return ans;
}
int main(){
    long long a,b,c;
    cin>>a>>b>>c;
    cout<<a<<"^"<<b<<" mod "<<c<<"="<<quickpower(a,b,c);
    return 0;
}

这个是网上找视频看的,基本算是抄的。写一下抄来的思路:

b&1相当于b%2==1,b>>=1相当于b/=2

还会用到取模运算的性质

快速幂是一种将底数平方,指数减半的简化运算的方法,对指数是奇/偶存在各自的处理方式,本题又多了一个求模,所以可以在简化运算的同时运用(ab)%p=(a%p)(b%p)%p,嗯,就这样。

3.P2249 二分查找

二分啊二分,好难好难,头秃好几天,到现在也没太搞清楚。这道题是自己上网找完视频也不太懂,然后边对着答案改自己敲好的写完的。基本全是抄的。

#include<iostream>
using namespace std;
int main(){
    int n,m; cin>>n>>m;
    //int l=1,r=n,k; 为啥放在这里就过不了?
    int a[1000010];
    for(int i=1;i<=n;i++) cin>>a[i];

    while(m--){ 
        int l=1,r=n,k;
        cin>>k;
        while(l<r){
            int mid=l+r>>1;
            if(a[mid]>=k) r=mid;
            else l=mid+1;
        }
        if(a[l]!=k){
            cout<<"-1 ";
            continue;
        }
        else cout<<l<<' ';
    }
    return 0;
}

写一下我对抄来的这个思路的理解:
二分法就是通过移动左/右边界值使得要查找的区间不断减半,最终确定所求值。现在觉得这个题挺简单的,纯粹的二分题。后面的二分答案的那种题就感觉有点抽象,难以理解。

有同学回答了注释里的问题,也贴上来吧。 

 4.P1824 进击的奶牛-二分答案

看了好久讲二分的博客,好难好难,看一遍,自己敲一遍,对着改。然后重复。也没写几个题。

这道题类似于博客里讲的跳石头和丢瓶盖,所以按照他们的思路自己敲了一下。感觉只是换了个皮,所以也基本就是抄的了(笑哭,四个题抄了三个半)。但是也算是第一道自己AC的算法题吧,别的多少都在看直接的答案,这个起码看的是别的题的答案然后触类旁通(虽然两个题很像很像)

#include<iostream>
#include<algorithm>
using namespace std;

const int N=1e5+10;
long long n,c,a[N];

int check(int mid){
    int i=1,now=1,num=0;
    while(i<n){
        i++;
        if(a[i]-a[now]>=mid) {now=i; num++;}
    }
    if(num+1>=c) return 1;
    return 0;   
}

int main(){
    cin>>n>>c;
    for(int i=1;i<=n;i++) cin>>a[i];
    sort(a+1,a+1+n);
    int l=0,r=a[n];
    while(l<r){
        int mid=l+r+1>>1;
        if(check(mid)) l=mid;
        else r=mid-1;
    }
    cout<<l;
    return 0;   

}

思路:求两头牛中间最小距离的最大值,那么按照博客里一直强调的,......就该>=mid,先对坐标进行排序,然后确定左右边界值,写好二分的模板(也是从博客学的)后,就该完善check函数了。

牛牛的间距,自然应该用坐标差来表示,既然有差,就要有两个变量i和now,还要有一个计数的num(表示加入一段间隔,所以后面用num+1表示牛的头数)。

想象一下,地上一列灯,要每隔一定间距点亮一盏,第一盏已亮。一个人从第一盏开始往后走,自然是走出至少为mid的距离时才点亮下一盏灯,这中间略过的灯就是跳石头那道题移走的石头,这些点亮的灯,就是丢瓶盖那道题里拿出的瓶盖,和进击的奶牛这道题里放置愤怒牛牛的牛棚。当间距满足>=mid后,num就可以+1,此时now=i,即为从该盏灯开始继续进行下一次的判定:走出>=mid的距离后,再次点亮第三盏灯。以此类推,如果走完全程,放红温牛牛的棚子(num+1,不是num)大于c,也就是说放得下,那就return 1,再次进行判定,直到num+1==c时,此时执行语句r=mid-1,但输出的是l,所以就可以输出对应num+1=c时的l值,这个值就是通过二分找出来的最小间距的最大值。

感悟:

二分答案往往需要处理两个东西,第一个是最值,第二个是二分。

最值的处理,就是check(mid)函数中的内容,这部分往往需要用到题中的距离、数目等等等等花里胡哨的数目(设为c),就是你的check函数要根据主函数中二分模板给出的mid值,把题给的一大堆数据处理出若干个结果或者组(设为num),将num与c比较,然后给check函数设置对应的返回值,再进行二分。从而不断逼近要找的值。然后cout就可以了。

掌握的极其差劲,还要再写写过的题。我不是那种天赋异禀的人,先保证做一道能理解一道吧。

贴一下那个博客里二分的模板:

感想:

别人在9028看视频,手搓算法题。我在9028搜集好几个答案,然后给自己写的加点这个改点那个。别人在学习,我在做实验,哈哈哈哈。我这个为什么错了,我的程序加上答案的什么就可以对了?我的什么地方是对的,答案里的什么替换成我写的也是可以过的?这周大概就是这样的思路了,所以一个题有可能会交几十遍,因为一直在试怎么可以过,怎样过不了。

Acm好难啊!!慢慢来吧,加油加油。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值