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) 编辑 收藏 举报