刷题记录(NC18979 毒瘤xor,NC20860 兔子的区间密码,NC17857 起床困难综合症)

NC18979 毒瘤xor

题目链接 

关键点: 

1、首先看懂题目:该题是给出一个数组,给出数组区间,求一个数x与该区间所有的数进行异或,之后所求出的最大值,那么最好情况是每一位的异或都为1

2、因为异或运算位与位之间互不影响,所以我们可以一位一位的考虑,如果在一个区间里的所有数,某个位的1多,那么就异或0(1^0=1, 0^0=0),同理,如果0多,就异或1

3、那么我们就要求出每一位的01数量,想到前缀和,sum[i][j]数组表示第前i个数字的第j位的1的数量,在输入数组的时候,就开始处理前缀和。

4、处理前缀和时要注意

sum[i][j] = sum[i-1][j];
   if ((a[i]>>j)&1==1)
   sum[i][j]++;

5、最后根据输入的区间,算出区间里的某位1的数量,如果1的数量小于区间里的数的一半,那么答案就在该位取1

完整代码:

# include <iostream>
# include <algorithm>
# include <cstdio>
using namespace std;
int n;
int a[100000+10];
int sum[100000+10][40];
int main()
{
    scanf("%d", &n);
    for (int i=1; i<=n; i++)
    {
        scanf("%d", &a[i]);
        for (int j=0; j<31; j++)
        {
            sum[i][j] = sum[i-1][j];
            if ((a[i]>>j)&1==1)
                sum[i][j]++;
        }
    }
            
    int x;
    scanf("%d", &x);
    for (int i=1; i<=x; i++)
    {
        int t, w, ans=0;
        scanf("%d%d", &t, &w);
        int total=0;
        for (int j=0; j<31; j++)
        {
            total = sum[w][j]-sum[t-1][j];
            if (total*2<w-t+1)
                ans |= (1<<j);
        }
        printf("%d\n", ans);
    }
    
    
    
    return 0;
}

NC20860 兔子的区间密码

题目链接 

关键点: 

1、题目大意为,给出一个区间,求该区间里的两个数异或的最大值

2、每一位进行异或所得出的值最好要为1,那么我们发现在一个区间的两个端点上,从最高位开始往后的位上,如果位上数字相同,那么取的两个数也只能取相同,因此异或出来就为0,

如果不同,那么就可以一个取1,一个取0(该位上),之后所有位取1的后面就可以全为0,取0的后面就全为1,这样不仅保证了数字在区间内,还保证了尽量多的可以异或为1,因此我们可以

找出端点两个数从最大位到最小位的(二进制),第一个不同的数,从这个位置开始(包括)全为1到结尾

 3、由于题目给出区间数字过大,答案和区间要开long long

完整代码

# include <cstdio>
# include <iostream>
using namespace std;
typedef long long ll;
int n;
int main()
{
    scanf("%d", &n);
    for (int i=1; i<=n; i++)
    {
        ll l, r;
        scanf("%lld%lld", &l, &r);
        ll pos=-1, x=1;
        for (int j=63; j>=0; j--)
        {
            int a = (l>>j)&1;
            int b = (r>>j)&1;
//             cout<<"a"<<a<<" "<<"b"<<b<<" "<<endl;
            if (a!=b)
            {
               pos = j; 
                break;
            }
        }
        cout<<(x<<(pos+1))-1<<endl;
    }
    
    
    
    return 0;
}

NC17857 起床困难综合症

题目链接 

关键点: 

1、大致题意为,给出一个值,在经过若干计算后,要使该值最大

2、同样的,因为&,|,^,这些位运算只与当前位有关,因此我们可以只考虑当前位,求可以得到的最大攻击值

3、并且每一位我们可以取0或者1,那么我们可以将所有的位都为0和1,去试一下经过所有运算后的结果,该位为0的结果为1,那么答案的该位就为1(因为初始值是从0-m,所以这样取不会超过该范围),如果不为1,那么看一下1的结果是否为1,如果为1那么还得在保证在范围内,才能取1

4、最后一点,我们将所有位用0和1去尝试,可以转换成用0(天然的所有位为0)和-1(所有位为1)去直接进行运算即可

完整代码

# include <cstdio>
# include <iostream>
using namespace std;
int n, m;
int main()
{
    scanf("%d%d", &n, &m);
    int a = 0, b=-1, pos=0;
//     while (m)
//     {
//         b|=(1>>pos);
//         m/=2;
//     }
    for (int i=1; i<=n; i++)
    {
        string s;
        int x;
        cin>>s>>x;
        if (s=="AND")
        {
            a&=x;
            b&=x;
        }
        else if (s=="OR")
        {
            a|=x;
            b|=x;
        }
        else
        {
            a^=x;
            b^=x;
        }
    }
    int x=0;
    for (int i=0; i<31; i++)
    {
        if ((a>>i)&1)
        {
            x|=(1<<i);
        }
        else
        {
            if (((b>>i)&1)&&((1<<i)<=m))
            {
                x|=(1<<i);
                m-=(1<<i);
            }
        }
    }
    printf("%d", x);
        
    
    
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值