Logo 昊然的博客

博客

2023年4月月赛2题解

2023-04-16 17:00:37 By 昊然

A题 奇偶数 题目链接

首先,看样例就知道:绝对不能暴力。

那怎么办?——二分呗!

二分应该都会,但如果代码这么写:

#include<bits/stdc++.h>
using namespace std;
int n,k;
int main()
{
    scanf("%d%d",&n,&k);
    int l=1,r=n,mid;
    while(l<r)
    {
        mid=(l+r+1)>>1;
        if(check(mid))
            l=mid;
        else
            r=mid-1;
    }
    printf("%d\n",r);
    return 0;
}

恭喜你,大鸭蛋。

为什么?拿样例数据2(11 6)一试就知道了:3不符合,但4符合,这是什么鬼?难道二分不对?

遇事不能慌,先手算一下,设x=mid:

如果x为偶数,则该数字会在path(x)、path(x+1)、path(2x)、path(2x+1)、path(2x+2)、path(2x+3)、path(4x)、path(4x+1)……中出现

如果x为奇数,则该数字会在path(x)、path(2x)、path(2x+1)、path(4x)、path(4x+1)、path(4x+2)、path(4x+3)、path(8x)……中出现

发现什么了吗?

即便奇数比偶数小,也有可能是偶数符合,奇数不符合!

怎么解决?很简单,先判断一下mid+1即可,附程序:

#include<bits/stdc++.h>
using namespace std;
int n,k;
int main()
{
    scanf("%d%d",&n,&k);
    int l=1,r=n,mid;
    while(l<r)
    {
        mid=(l+r+1)>>1;
        if(check(mid+1))
            l=mid+1;
        else
            if(check(mid))
                l=mid;
            else
                r=mid-1;
    }
    printf("%d\n",r);
    return 0;
}

程序主函数解决了,怎么写check?

这里提供两种做法:

1.深搜(dfs)/宽搜(bfs)

思路:从mid往上推,分两种情况:

1.如果mid是偶数,则mid+1和mid*2都有可能

2.如果mid是奇数,则只有mid*2有可能,因为mid+1是偶数,不会-1

问题:怎么存储求解出的数字?

排除重复数据,肯定会有人想到用桶,那么对不起,再友好的数据也会让桶炸掉,80分,这怎么办?

可以使用set,效率高,自带排除重复功能。

好了,思路清晰,写就是啦!(附宽搜代码)

bool check(int mid)
{
    queue<int> q;
    set<int> s;
    q.push(mid);
    s.insert(mid);
    while(!q.empty())
    {
        if(s.size()>=k)
            return true;
        int l=q.front();
        q.pop();
        s.insert(l);
        if(!(l&1)&&l+1<=n)
            q.push(l+1);
        if(l<<1<=n)
            q.push(l<<1);
    }
    return s.size()>=k;
}

特别注意!!!这个思路不是正解,只是数据友好,所以能AC(不过宽搜在小数据情况下可能比正解还快)

2.(我也说不清它叫啥)

思路:深搜宽搜都过不了10^9 10^9,那有没有方法可以呢?

有,还真有!

之前手算了经过mid的数字,这里再写一下:

x是偶数:path(x)、path(x+1)、path(2x)、path(2x+1)、path(2x+2)、path(2x+3)、path(4x)、path(4x+1)……

x是奇数:path(x)、path(2x)、path(2x+1)、path(4x)、path(4x+1)、path(4x+2)、path(4x+3)、path(8x)……

没发现规律?没事,化简一下:

x是偶数:path(x)-path(x+1)、path(2x)-path(2x+3)、path(4x)-path(4x+7)、path(8x)-path(8x+15)……

x是奇数:path(x)、path(2x)-path(2x+1)、path(4x)-path(4x+3)、path(8x)-path(8x+7)……

还没发现规律?没事,分成左端点和右端点来看:

x是偶数:左端点:path(x)、path(2x)、path(4x)、path(8x)、path(16x)……

右端点:path(x+1)、path(2x+3)、path(4x+7)、path(8x+15)、path(16x+31)……

x是奇数:左端点:path(x)、path(2x)、path(4x)、path(8x)、path(16x)……

右端点:path(x)、path(2x+1)、path(4x+3)、path(8x+7)、path(16x+15)……

发现规律了吧!!!

左端点:x、2x、2(2x)、2[2(2x)]……

右端点:偶数:x+1、2(x+1)+1、2[2(x+1)+1]+1……

奇数:x、2x+1、2(2x+1)+1、2[2(2x+1)+1]+1……

可以直接计算左端点(l)和右端点(r),再把总数+=(r-l+1)即可。

代码实现:

bool check(int mid)
{
    int l=mid,r,sum=0;
    switch(l&1)
    {
        case 0:r=mid+1;                            break;
        case 1:r=mid;                            break;
    }
    while(l<=n&&sum<k)
    {
        sum+=min(r,n)-l+1;
        l<<=1;
        r<<=1;
        r++;
    }
    return sum>=k;
}

最后,附正解的完整AC代码:

#include<bits/stdc++.h>
using namespace std;
int n,k;
bool check(int mid)
{
    int l=mid,r,sum=0;
    switch(l&1)
    {
        case 0:r=mid+1;                            break;
        case 1:r=mid;                            break;
    }
    while(l<=n&&sum<k)
    {
        sum+=min(r,n)-l+1;
        l<<=1;
        r<<=1;
        r++;
    }
    return sum>=k;
}
int main()
{
    scanf("%d%d",&n,&k);
    int l=1,r=n,mid;
    while(l<r)
    {
        mid=(l+r+1)>>1;
        if(check(mid+1))
            l=mid+1;
        else
            if(check(mid))
                l=mid;
            else
                r=mid-1;
    }
    printf("%d\n",r);
    return 0;
}

B题 最强大脑 题目链接

先扯一句题外话:图片里的游戏叫祖玛。

这题超级简单,一边输入一边判断即可。

附AC代码:

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int n,k1,k2=0,sum=0,ans=0;
    scanf("%d",&n);
    while(n--)
    {
        scanf("%d",&k1);
        if(k1==k2)
            sum++;
        else
        {
            if(ans<sum)
                ans=sum;
            sum=1;
        }
        k2=k1;
    }
    if(sum>ans)
        ans=sum;
    printf("%d\n",ans);
    return 0;
}

C题 三角形问题 题目链接

这题n是5000,不大,但是暴力枚举肯定是不行的,要优化。

怎么优化?——找规律

看第一行,很容易就能找到规律,第n+1行的第一个数字为:

\begin{equation} \frac{n*(n-1)}{2}+1 \end{equation}

规律出来了,直接暴力枚举至要查询的数字或位置即可

注意事项:

1.公式算的是第n+1行,不是第n行!!!

2.搞清楚那条边是x,哪条边是y!!!(我就差点搞反了,见AC代码)

#include<bits/stdc++.h>
using namespace std;
void build1(int n)
{
    int k,x,y;
    for(k=0;k*(k+1)/2+1<=n;k++);
    x=k;
    k-=1;
    k=k*(k+1)/2+1;
    for(y=1;x>0;x--,y++)
    {
        if(n==k)
        {
            printf("%d %d\n",y,x);        //此处差点搞反
            return;
        }
        k++;
    }
}
void build2(int x,int y)
{
    y--;
    int n=y*(y+1)/2+1,k;
    y+=2;
    for(k=1;k<x;k++)
    {
        n+=y;
        y++;
    }
    printf("%d\n",n);
}
int main()
{
    int t;
    scanf("%d",&t);
    if(t==1)
    {
        int n;
        scanf("%d",&n);
        build1(n);
    }
    else
    {
        int x,y;
        scanf("%d%d",&x,&y);
        build2(x,y);
    }
    return 0;
}

题外话:

1.题解制作不易,不喜勿喷

2.禁止抄袭!!!(望老师严查)

厉害的贾永菁

2022-07-02 16:50:38 By 昊然

77449 #15. 在屏幕上输出hi 贾永菁 Compile Error / / C++ 248b 2022-06-11 18:17:17

昊然 Avatar