蓝桥杯22年第十三届省赛-选数异或|暴力、线性dp

题目链接

5.选数异或 - 蓝桥云课 (lanqiao.cn)

思路:

重要结论:a^b=x可得a^x=b

暴力做法:

每次遍历给定区间的元素,将出现的元素建一个表表示这个数在区间里出现过,同时查这个元素a的a^x是否在表里,出现则区间里能找到一对合法的数。过60%或40%数据。

注意:每次map要清空。

dp做法:

f[i]表示以i为右端点(结尾)的子数组 ,它最近的 能够选出异或为x的一对数字的 左边那个位置 为 f[i](是这个左边位置最近,不是右边位置出现得最近,因为左边位置最近的话右边位置必然在这个左边位置的右边)。

要让一个区间包括这对合法的数,那么就要让这个 区间左端点小于这对数左边数的位置,而右端点大于这对数右边数位置 。而右端点大于这对数右边数位置 这个条件,设这个dp数组的时候 作为前提满足的。 这样就找到了一个边界,如果给的以r为右端点的区间左端点小于f[r],那么 这段区间就包括了至少一对数字能够异或为x,状态转移公式为:

f[i]=max(f[i-1],p[(a[i]^x)]);

要么从前面已经找到的选,要么是自己新产生的(通过异或性质计算:a^b=x可得a^x=b,查map是否前面出现过这个a^x,出现过,就是自己产生的位置) ,

所以需要建一个 哈希表,存每个值对应的最大位置 (建表时如果有几个位置出现相同值,大的位置覆盖小的位置,因为求最近的位置,大的位置肯定更近)

代码:

暴力:

#include<bits/stdc++.h>
#define int long long
using namespace std;

const int N=1e5+10;

int n,m,x;
int a[N];
unordered_map<int,int> rr;
signed main(){
    
  //关同步!!!
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0); 
    
    cin>>n>>m>>x;
    for(int i=1;i<=n;i++){
        cin>>a[i];
    } 
    

    while(m--){
        int l,r;
        cin>>l>>r;
        if(l==r) printf("no\n");
        else{
            //每次要清零 
            rr.clear();
            int i;
            //如果 a^b=x,那么b^x=a,a^x=b。根据这个性质,在 这个区间里用map记录(注意:每次要清空!!)
            //一旦当前元素b的 对应 的 b^x=a在map里有记录,说明这个区间能找到异或为x的 
            for(i=l;i<=r;i++){
                if(rr.find((a[i]^x))!=rr.end()){
                    printf("yes\n");
                    break;
                }
                rr[a[i]]=1;
            }
            if(i>r) printf("no\n");
        }
    }
    
    
    return 0;
}

dp:

#include<bits/stdc++.h>
#define int long long
using namespace std;

const int N=1e5+10;

int n,m,x;
int a[N];

int f[N];
//注意这个哈希表的范围要根据Ai的大小开
int p[10*N+50000]={0};
signed main(){
    
  //关同步!!!
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0); 
    
    cin>>n>>m>>x;

    for(int i=1;i<=n;i++){
        cin>>a[i];
    } 
    
    for(int i=1;i<=n;i++){
        f[i]=max(f[i-1],p[(a[i]^x)]);
        p[a[i]]=i;
    }
    

    while(m--){
        int l,r;
        cin>>l>>r;
        if(l==r) printf("no\n");
        else{
            if(f[r]>=l)
            printf("yes\n");
            else
            printf("no\n");
        }
    }
    
    
    return 0;
}

                
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CGuts350

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值