ACM新手DAY 1 位运算与(矩阵)快速幂

题解

A - 最低位

题目:读入一个整数,对它的二进制形式进行操作:取从他的右边向左边第一个不为0的一段二进制数,转换成十进制形式。如,26的二进制形式是11010,取出10这一段,输出8。

  • 位运算
  • 用与运算1判断读入整数的二进制最后一位是不是1,用右移运算2实现二进制位右移比较
  • 逻辑部分:
int c(0);
        while(~ n & 1)//与运算
        {
            c++;
            n >>= 1;//右移运算
        }
        cout << pow(2, c)<<endl;//重新转换成十进制

B - 杰米和二进制序列(补题)

题目:找到k个整数,使得每个数的幂的2的总和等于数n,并且答案中的最大整数尽可能小。 由于可能有多个答案,因此要求输出按字典顺序排列的最大答案。

  • 位运算
#include<cstdio>
int ans[100005],v[100005];
int main()
{
    long long n,k;
    scanf("%lld%lld",&n,&k);
    int i,index=0,cnt=0;//cnt记录已经拆了多少个
    //先拆成2进制
    while(n)
    {
        if(n%2==1)
        {
            v[index]=1;
            cnt++;
        }
        index++;
        n/=2;
    }
    //如果cnt已经大于k,cnt当前记录的是最少要拆几个
    if(cnt>k)
    {
        printf("No\n");
        return 0;
    }
    printf("Yes\n");
    int minn;
    //二进制顺序反转
    for(i=0; i<index; i++)
    {
        ans[i]=v[index-i-1];
    }
    //找到最小的二进制为1的数minn,最大的不够拆了从minn开始拆
    for(i=0; i<index; i++)
    {
        if(ans[i])
            minn=i;
    }
    //从头开始拆(保证了y尽可能小)
    for(i=0;; i++)
    {
        if(cnt==k)
            break;
        if(ans[i]+cnt<=k)//这个判断是精髓部分
        {
            cnt+=ans[i];
            ans[i+1]+=ans[i]*2;
            ans[i]=0;
        }
        else
            break;
        if(i+1>minn)
            minn=i+1;   //更新最小值下标
    }
    //从最小值开始拆(保证了按照字典树最小)
    for(;; i++)
    {
        if(cnt==k)
            break;
        cnt++;
        ans[minn]--;
        ans[minn+1]+=2;
        minn++;
    }
    //找到输出下标上限
    int a=index>i?index-1:i;
    a=minn>a?minn:a;
    //输出
    for(i=0; i<=a; i++)
    {
        while(ans[i])
        {
            ans[i]--;
            minn=index-i-1;
            printf("%d ",minn);
        }
    }
    printf("\n");
    return 0;
}

C - 麦哲伦的异或

题目:对一个长度为n的数组,求所有的 x o r ( A i + A j ) ( 1 ≤ i , j ≤ n ) xor(Ai + Aj)(1≤i,j≤n) xor(Ai+Aj)(1ijn)。阵列B的xor定义为 B 1 x o r B 2... x o r B n B1 xor B2 ... xor Bn B1xorB2...xorBn

  • 位运算
  • 异或运算3的性质运用( a x o r a = 0 , 0 x o r a = a a xor a = 0, 0 xor a = a axora=0,0xora=a)
  • 注意点:He wants to know the xor of all (Ai+Aj)(1≤i,j≤n) 其实是指所有的 ( A i + A j ) (Ai+Aj) Ai+Aj的组合的异或。如,当n = 3, 输出的就是 ( A 1 + A 1 ) x o r ( A 1 + A 2 ) x o r ( A 1 + A 3 ) x o r ( A 2 + A 1 ) x o r ( A 2 + A 2 ) x o r ( A 2 + A 3 ) x o r ( A 3 + A 1 ) x o r ( A 3 + A 1 ) x o r ( A 3 + A 2 ) x o r ( A 3 + A 3 ) (A1 + A1) xor (A1 + A2) xor (A1 + A3) xor (A2 + A1) xor (A2 + A2 ) xor (A2 + A3) xor (A3 + A1) xor (A3 + A1) xor (A3 + A2) xor (A3 + A3) A1+A1xor(A1+A2)xor(A1+A3)xor(A2+A1)xor(A2+A2)xor(A2+A3)xor(A3+A1)xor(A3+A1)xor(A3+A2)xor(A3+A3)
  • 多组输入
    int T;
    cin >> T;
    while(T--){}
  • 逻辑部分(最容易想到的就是暴力求所有的异或,但是会超时的。再想想,其实根据 a x o r a = 0 , 0 x o r a = a a xor a = 0, 0 xor a = a axora=0,0xora=a可以想到,最后求得是所有 ( A i + A j ) , i = j (Ai+Aj),i=j Ai+Aj,i=j时的组合的异或):
        cin >> n >> m >> z >> l;
        ll A = 0;
        for(ll i=2; i<=n; i++)
            {
              A = (A * m + z) % l;
              s ^= (A+A);
            }//这里要注意怎么排除情况,减小运算量
        cout << s << endl;

D - a的b次(样题)

题目:给你两个数A和B,计算A的B次方,输出A的B次方的最后三位数所表示的整数。

  • 快速幂(整数)
  • 主要需要理解怎么利用位运算进行快速幂计算
  • a=0,b=0时停止的多组输入
while(cin>>a>>b && a && b)
    {
        cout << quickPow(a, b) <<endl;
    }
  • 快速幂函数部分
  • 示范:对于 x 11 x^{11} x11,11→ ( 1011 ) 2 (1011)_2 (1011)2,过一遍快速幂函数
n&1resx
1 x x x x 2 ( x 2 1 ) x^2(x^{2^1}) x2(x21)
1 x ∗ x 2 x*x^2 xx2 x 4 ( x 2 2 ) x^4(x^{2^2}) x4(x22)
0 x ∗ x 2 x*x^2 xx2 x 8 ( x 2 3 ) x^8(x^{2^3}) x8(x23)
1 x ∗ x 2 ∗ x 8 x*x^2*x^8 xx2x8 x 16 ( x 2 4 ) x^{16}(x^{2^4}) x16(x24)
#define ll long long
ll quickPow(ll x, ll n)//n是指数
{
    ll res = 1;
    while(n > 0)
    {
        if(n & 1) res = res * x % 1000;//这里根据题意题目每次取后三位,应用到其他题目时可以灵活变通
        x = x * x % 1000;
        n >>= 1;
    }
    return res;
}

E - 取模运算

题目:给出一个整数M以及H组A和B,求 ( A 1 B 1 + A 2 B 2 + . . . + A H B H ) m o d M . (A_1^{B_1}+A_2^{B_2}+ ... +A_H^{B_H})mod M. (A1B1+A2B2+...+AHBH)modM.

  • 快速幂(整数)
  • 快速幂的应用,主要在于对m的取模
  • 输入部分(Z是多个实例输入):
    int z;
    cin >> z;
    while(z--)
    {
        int m, tt, s(0);
        cin >> m >> tt;
        while(tt--)
        {
            int a, b;
            cin >> a >> b;
            s += quickpow(a, b, m);
        }
        cout << s % m << endl;
    }
  • 快速幂函数部分:
ll quickpow(ll x, ll n, ll m)
{
    ll res = 1;
    while(n > 0)
    {
        if(n & 1) res = res * x % m;
        x = x * x % m;
        n >>= 1;
    }
    return res;
}

F - 一系列数字

题目:n个序列(等差/等比数列),给出每个序列的前三个,求数列中指定位置的数

  • 快速幂(整数)
  • 快速幂的应用
  • 注意点:对200907取模。
  • 多组输入
    ll N;//ll is long long
    cin >> N;
    while(N--)
  • 判断是等差还是等比数列
        ll a, b, c, k;
        cin >> a >> b >> c >> k;
        if((b-a) == (c-b))
            cout << (a + (k - 1) * (b - a)) % 200907<<endl;
        else
            cout << a * quickpow(b/a, k-1) % 200907 << endl;
  • 快速幂函数部分
ll quickpow(ll x, ll n)
{
    ll res = 1;
    while(n > 0)
    {
        if(n & 1) res = res * x % 200907;
        x = x * x % 200907;
        n >>= 1;
    }
    return res;
}

G - Fibonacci

题目:求 F n F_n Fn
在这里插入图片描述

  • 快速幂(矩阵)
  • 矩阵快速幂的应用,可以由数的快速幂扩展过来
  • 结构体的使用:
struct matrix
{
    int m[3][3];
};
  • 矩阵相乘函数部分:
matrix mul(matrix a,matrix b)
{
    matrix c;
    memset(c.m,0,sizeof(c.m));
    int i,j,k;
    for(i=1; i<=n; i++)
        for(j=1; j<=n; j++)
            for(k=1; k<=n; k++)
            {
                c.m[i][j]+=a.m[i][k]*b.m[k][j];
                c.m[i][j]%=10000;
            }
    return c;
}
  • 快速幂函数部分:
matrix quickpower(matrix ans,int k)
{
    matrix res;
    memset(res.m,0,sizeof(res.m));//矩阵的初始化
    for(int i=1; i<=n; i++) res.m[i][i]=1;//数的快速幂是取1,矩阵的是取单位矩阵

    while(k)
    {
        if(k&1)
            res=mul(ans,res);
        ans=mul(ans,ans);
        k>>=1;
    }
    return res;
}

H - 神奇的函数

题目:接受一个矩阵 A 和一个正数 k,计算 A k A^k Ak,然后输出这个矩阵对角线元素之和。

  • 快速幂(矩阵)
  • 矩阵快速幂的应用,与G题的区别主要在于作为底数的矩阵变成了未知的,需要输入
  • main函数内容的变化:
int main()
{
    int sum, k, t;
    cin>>t;
    while(t--)
    {
        cin>>n>>k;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
            {
                cin>>A.m[i][j];
                A.m[i][j]%=9973;
            }

        B=quickpower(A,k);

        sum=0;
        for(int i=1;i<=n;i++)
        {
            sum+=B.m[i][i];
            sum%=9973;
        }
        cout<<sum<<endl;
    }
    return 0;
}

  1. 一个比特(Bit)位只有 0 和1 两个取值,只有参与&运算的两个位都为 1 时,结果才为 1,否则为 0。例如1&1为 1,0&0为 0,1&0也为 0 ↩︎

  2. 右移运算符>> 用来把操作数的各个二进制位全部右移若干位,低位丢弃,高位补符号位。 ↩︎

  3. 参与 ^运算两个二进制位不同时,结果为 1,相同时结果为 0。例如0 ^ 1为1,0 ^ 0为0,1 ^ 1为0。 ↩︎

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值