salute-to-Mr-Lynch

导航

2024牛客暑期多校训练营2 E题GCD VS XOR C题Red Walking on Grid

题目:E题GCD VS XOR

 

    这个题目大意就是让你找出一个数y,让它与x的最大公因数等于x与y进行异或运算的结果。

题意分析:

  从我刚开始作为一个新手的角度出发,我第一想法就是暴力枚举,然后判断gcd(x,y)是否等同于x⊕y。

void solve() {
    int x;
    cin >> x;
 
    for (int i = 1; i < x; i++) {
        if (__gcd(i, x) == (x ^ i)) {
            cout << i << endl;
            return;
        }
    }
    cout << -1 <<endl;
}

  但这种算法时间复杂度为O(N),这种算法时间复杂度还是太高了。现在我们想想有没有方法可以优化一下这种算法。

    遇到这种位运算类型的题目,我们将它化成二进制的形式将会比较好写一点,比如我们可以随便取一个数,假设x化为二进制的形式后是1010100,我们从异或的角           度思考,相同为0,不同为1,那么最后异或出来的结果必然是由2k相加所组成的数。再从最大公因数的角度思考,在二进制的形式,在1后面每增加一个0,相当于乘             以2倍,那么理所当然以二进制中的最低位1以及后面的0组成的数就是x的以2k形式的最大公因数。既然已经知道了最大公因数,我们就可以反推出来y,只需让最大公           因数的位置全部置为0,其他位置保留原样即可。就比如x=1010100 -> y=1010000。

    思路已成,代码随后就来。这里的主要难点是如何取最低位的1。

 

#include<iostream>
using namespace std;
typedef long long ll;

void solve(ll x)
{
    if(!(x&(x-1)))cout << -1 << endl;//为了预防2^k这种情况(1000....) 
    else
    {
        ll h=1,p=x;
        while(p%2==0)
        {
            h*=2;//依次取最低为位,然后合成为一个数。
            p/=2;
        }
        cout << x-h << endl;    
    }
}

signed main()
{
    int t;
    cin >> t; 
    while(t--)
    {
        ll x;
        cin >> x;
        solve(x);
    }
    return 0;
} 

    这个算法的时间复杂度为O(logN),这个复杂度其实已经可以满足题目要求了,但是其实我们还可以再优化一下。下面我将介绍 lowbit(x)算法,这个算法就是用来取最低位的算法,lowbit(x)=(x&(-x))。

    负数在电脑中的存储方式是以原码的补码形式存储,即原码各位取反,最后再加上1。有上面可知原码的最低形式是1000....这样的形式出现的,将其取反变成0111....,再将其加1,变成1000.....,最后进行&运算,

    将会保留最低位的部分,而最低位前面的部分,因为原码全部取反的原因,全部变成0了,最后只会保留最低位的部分。

    这是完整的代码,其时间复杂度为O(1)。

#include<iostream>
using namespace std;
typedef long long ll;

ll solve(ll x)
{
    return x-(x&(-x));
}

signed main()
{
    int t;
    cin >> t; 
    while(t--)
    {
        ll x;
        cin >> x;
        ll y=solve(x);
        if(y>0){
            cout << y << "\n";
        }
        else cout << -1 << "\n";
    }
    return 0;
} 

 

C题-Red Walking on Grid

题意解读:

  题目给出了一个(2 x n)大小的网格,里面有红色格子和白色格子,小红首先可以选择一个红色格子作为初始位置,然后可以向前后左右移动,当从一个红色格子离开后,这个格子立马变成白色。小红想要知道他能在这个网格中最多走几步。如果网格中没有红色格子,请你输出0。

 

解题思路:

  因为题目中规定了,小红可以任意选取一个格子作为初始位置。从最大的角度想,不妨从最右边的红色格子开始计数。对小红来说,每到达一个格子,她是如何来到这个格子对她未来到哪个字没有任何影响,因此我们用dp来做这道题。对于任意一个格子来说,它可以由左边的格子递推而来,或者由上面的格子递推而来,因此我们可以获得递推方程dp[0][i] = dp[0][i-1]+1,dp[0][i]=max(dp[0][i] , dp[1][i-1]+2),对下面的格子来说亦是如此。

  

代码编写如下:

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;//推荐一定要比题目给的范围打几个数,不然怎么错的都不知道。
typedef long long ll;
char a[2][N];
int dp[N][2];//打代码是遇到一个逆天错误,我发现有时候,仅仅是交换一下a[2][N]和dp[N][2]的位置都可能出错,我猜测可能是数据N太大,发生了数据越界的情况,反正就是很难受,因此N最好要比给的范围打几个数。
void solve()
{ 
    int n;
    cin >> n;
    int ans=0;
    for(int i=0;i<n;i++)cin>> a[0][i];
    for(int i=0;i<n;i++)cin>> a[1][i];
    for(int i=0;i<n;i++)
    {
        int x=(a[0][i]=='R');
        int y=(a[1][i]=='R');
        if(x)dp[i][0]=dp[i-1][0]+1;
        if(y)dp[i][1]=dp[i-1][1]+1;
        if(x&&y)
        {
            dp[i][0]=max(dp[i][0],dp[i-1][1]+2);
            dp[i][1]=max(dp[i][1],dp[i-1][0]+2);
        }
        int s=max(dp[i][0],dp[i][1])-1;
        ans=max(ans,s);
    }
    cout << ans << "\n";
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    solve();
    return 0;
 } 

 

  

posted on 2024-07-23 20:43  kiyotaka-ayanokoji  阅读(23)  评论(0编辑  收藏  举报