算法竞赛进阶指南 0x01 位运算 牛客

资源链接
栗个flag,从今天开始学这本书把牛客上的题全部更新完

第一篇主要是位运算快速幂之类的简单知识点

A题
题目大意,求ab对c取模,数据1e9

显然快速幂就可以很容易做出来,如果你不知道快速幂下面的代码会帮到你

//link:https://ac.nowcoder.com/acm/contest/996/A
//有可能p取1,都要对p去莫
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<functional>
#include<cassert>
#include<cstdlib>
#include<cstring>
#include<string>
#include<ctime>
using namespace std;
#define inf 0x3f3f3f3f
#define endl '\n'
#define rep(a,b,c) for(int (a)=(b);(a)<=(c);++(a))
#define res(a,b,c) for(int (a)=(c);(a)>=(b);--(a))
typedef long long ll;
inline int _read()//快读
{
    int t1=0,t2=1; char c=getchar();
    while(!isdigit(c)){if(c=='-') t2=-1;c=getchar();}
    while(isdigit(c)){t1=t1*10+c-'0';c=getchar();}
    return t1*t2;
}
const int mod=1e9+7;
const int M=200020,N=100010;
ll n, m, p;
ll quick_pow(ll x,ll y,ll p){//快速幂
    ll res1 = 1%p;//注意这里要对p去摸,我在这里wa了两三次
    while(y){
        if(y&1){
            res1 *= x;
            res1 %= p;
        }
        x *= x;
        x %= p;
        y >>= 1;
    }
    return res1%p;
}
int main()
{
    cin >> n >> m >> p;
    cout<<quick_pow(n,  m,  p);//平平无奇的结束
    return 0;
}


B题

题目是英文,大致意思是给出多组样例多组数据,每个样例对对多组数据求幂后去模

和上面的解法一致不在赘述

//link:https://ac.nowcoder.com/acm/contest/996/B
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<functional>
#include<cassert>
#include<cstdlib>
#include<cstring>
#include<string>
#include<ctime>
using namespace std;
#define inf 0x3f3f3f3f
#define endl '\n'
#define rep(a,b,c) for(int (a)=(b);(a)<=(c);++(a))
#define res(a,b,c) for(int (a)=(c);(a)>=(b);--(a))
typedef long long ll;
inline int _read()
{
    int t1=0,t2=1; char c=getchar();
    while(!isdigit(c)){if(c=='-') t2=-1;c=getchar();}
    while(isdigit(c)){t1=t1*10+c-'0';c=getchar();}
    return t1*t2;
}
const int mod=1e9+7;
const int M=200020,N=100010;
ll quick_pow(ll x,ll y,ll p){
    ll res1 = 1%p;
    while(y){
        if(y&1){
            res1 *= x;
            res1 %= p;
        }
        x *= x;
        x %= p;
        y >>= 1;
    }
    return res1%p;
}
int main(){
    int t = _read();
    while(t--){
        int p = _read();
        int n= _read();
        ll ans = 0;
        while(n--){
            ll res1 = _read(),res2=_read();
            ans =(ans+ quick_pow(res1, res2, p))%p;
        }
        cout << ans << endl;
    }
    return 0;
}

C题

题目大意:求a*b对c取模 a,b,c都可以取到1e18

第一思路是高精度,但是这个是位运算的题高精很显然不太合适。
正确的揭发应该是按位取模
我们上幼儿园的时候就知道a*b == b个a 相加
所以我们可以让b依次向左移位,如果&1+=a,每次移位之后乘以2
是不是简简单单

//link:https://ac.nowcoder.com/acm/contest/996/C
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<functional>
#include<cassert>
#include<cstdlib>
#include<cstring>
#include<string>
#include<ctime>
using namespace std;
#define inf 0x3f3f3f3f
#define endl '\n'
#define rep(a,b,c) for(int (a)=(b);(a)<=(c);++(a))
#define res(a,b,c) for(int (a)=(c);(a)>=(b);--(a))
typedef unsigned long long ll;
inline ll _read()
{
    ll t1=0,t2=1; char c=getchar();
    while(!isdigit(c)){if(c=='-') t2=-1;c=getchar();}
    while(isdigit(c)){t1=t1*10+c-'0';c=getchar();}
    return t1*t2;
}
int main(){
    ll a = _read(), b = _read(), c = _read();
    ll res = 0;
    while(b){
        if(b&1){
            res =(res+ a)%c;
        }
        a =(a*2)%c;
        b >>= 1;
    }
    cout << res;
    return 0;
}

D题
题目大意:是求一个最短路径
从0->n每个点都必须经过问最小需要多少步,n<20;
给出每个边到另一个边所需要的距离,保证j->j==0
len(i->k) <= len(i->j) + len(j->k);
len(i->k) == len(i->j)
样例:
输入描述
一个正整数n,代表目标位置
接下来n行每行n个数
表示从i到j的距离
例如 1->1 == 0 1->2 == 2
4
0 2 1 3
2 0 2 1
1 2 0 1
3 1 1 0

4
可以点下链接看下题意

这是求最短Hamilton路径的一个模板题,一个人思考这个方法是很难想出来的。
大致解题思路,我们把一个整数转为二进制,假如000101相当于第一个和第三个已经走过,此时我们可以不断的把剩余的0更新为1用来表示接下来可以走的一个位置
我们可以开一个f[1<<n][n]的数组前面的用来存位置,后面的用来存目前所在的位置因此可知f[1][0]==0
所以可以从f[1][0]处开始dp
直接枚举0到1<<20

//link:https://ac.nowcoder.com/acm/contest/996/D
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<functional>
#include<cassert>
#include<cstdlib>
#include<cstring>
#include<string>
#include<ctime>
using namespace std;
#define inf 0x3f3f3f3f
#define endl '\n'
#define rep(a,b,c) for(int (a)=(b);(a)<=(c);++(a))
#define res(a,b,c) for(int (a)=(c);(a)>=(b);--(a))
typedef long long ll;
inline int _read()
{
    int t1=0,t2=1; char c=getchar();
    while(!isdigit(c)){if(c=='-') t2=-1;c=getchar();}
    while(isdigit(c)){t1=t1*10+c-'0';c=getchar();}
    return t1*t2;
}
int f[1 << 20][20]; //f[i][j]表示i的状态,为1则代表经过,为0则代表没经过。j代表当前所处的位置。
int weight[20][20]; //存储边的权值
int main()
{
    int n;
    cin >> n;
    rep(i,0,n-1){
        rep(j,0,n-1){
            int res1 = _read();
            weight[i][j] = res1;
        }
    }
    memset(f, 0x3f, sizeof f);//初始化,便于后面转移方程的使用
    f[1][0] = 0;
    rep(i,0,(1<<n)-1){
        rep(j,0,n-1){
            if((i>>j)&1){//如果j+1位为1,我们要得到如何到这个位置用的距离最小
                rep(k,0,n-1){
                    if(i-(1<<j)>>k&1){//这里枚举每一个可能来时的点
                        f[i][j] = min(f[i][j], f[i - (1 << j)][k] + weight[k][j]);//找到最小的距离
                    }//f[i][j]相当于目前在j位置然后i相当于是走了多少个点
                }//很简单吧
            }
        }
    }
    cout << f[(1 << n) - 1][n - 1] << endl;//输出每个点都走过并且在目标位置的点
    return 0;
}

E题
大致题意
两个数n,m你代表经过位运算的个数,m表示是可以取到的最大的正整数,要取正整数。
对于一个随机取得的数k,取最后结果得到的最大值,并输出这个最大值
样例
输入描述 第一行 两个数n,m
接下来n行 一个字符串一个数,表示运算方式,及对谁运算
3 10
AND 5
OR 6
XOR 7

1

大致思路,位运算就是一个位的运算并不牵扯另一位所以,我们只要对0和1分别求运算得到的结果就是我们需要的结果,如果两个都是1,则为0,仅有1为1,看是否可以在这一位取1。
所以我们只需要取一个全为0的数,和一个全为1的数。0 和 -1!!

//link:https://ac.nowcoder.com/acm/contest/996/E
#include<iostream>
#include<algorithm>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<functional>
#include<cassert>
#include<cstdlib>
#include<cstring>
#include<string>
#include<ctime>
using namespace std;
#define inf 0x3f3f3f3f
#define endl '\n'
#define rep(a,b,c) for(int (a)=(b);(a)<=(c);++(a))
#define res(a,b,c) for(int (a)=(c);(a)>=(b);--(a))
typedef long long ll;
inline int _read()
{
    int t1=0,t2=1; char c=getchar();
    while(!isdigit(c)){if(c=='-') t2=-1;c=getchar();}
    while(isdigit(c)){t1=t1*10+c-'0';c=getchar();}
    return t1*t2;
}
int op0 = 0, op1 = -1,ans=0;//一个全为0的数和一个全为1的数
int main(){
    int n=_read(), m=_read();
    while(n--){//对他两不断的进行运算
        string str;
       int  res;
        cin >> str >> res;
        if(str[0]=='A'){
            op0&=res;
            op1&=res;
        }
        else if(str[0]=='X'){
            op0^=res;
            op1^=res;
        }
        else {
            op0|=res;
            op1|=res;
        }
    }  
    res(i,0,29){
        if((op0>>i)&1){//按位求如果0为1直接加上这个数,反正判断1是否为1并且判断是否这位大于m
            ans += (1 << i);
        }
        else if((op1>>i)&1&&(1<<i)<=m){
            ans += (1 << i), m-=(1<<i);
        }
    }
    cout << ans;
    return 0;
}

over <\ _ \> (手动笑脸.gif)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值