牛客练习赛66B题解

前言

开始没想到异或这么多的性质,于是认为对于每个点\(u\),可以和它连边的点\(v\)的点权 \(a_v=a_u \oplus k\)(证明:\(\because\) \(a_u\oplus a_v =k\) \(\therefore\) \(a_u\oplus a_v \oplus a_u=a_u \oplus k\)\(a_v=a_u \oplus k\)),于是每次将同一个点权的点放在一起,跑一遍dijkstra,然后超时了……(赛后重新提交,显示case通过率为32.00%
放一下当初代码:

#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e6+10,MAXLIMIT=1048577;
int n,q,a[MAXN],ans,k,x,y;
vector<int> m[MAXLIMIT];
bool vis[MAXN];
int dis[MAXN];
int main(){
    scanf("%d %d",&n,&q);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        m[a[i]].push_back(i);
    }
    while(q--){
        scanf("%d %d %d",&k,&x,&y);
        memset(dis,~0xcf,sizeof(dis));
        memset(vis,0,sizeof(vis));
        dis[x]=0;
        vis[x]=1;
        ans=MAXN+10;
        priority_queue <pair<int, int> > q;
        q.push(make_pair(0,x));
        while(!q.empty()&&ans>n){
            int t=q.top().second; q.pop();
            int togo=a[t]^k;
            for(int i=0;i<m[togo].size();i++){
                if(!vis[m[togo][i]]){
                    dis[m[togo][i]]=dis[t]+1;
                    if(m[togo][i]==y){
                        ans=dis[t]+1;
                        break;
                    }
                    vis[m[togo][i]]=1;
                    q.push(make_pair(dis[m[togo][i]],m[togo][i]));
                }
            }
        }
        if(ans<=n) printf("%d\n",ans);
        else printf("-1\n");
    }
    return 0;
}

思路

实际上很简单,答案只有 -1,1,2 三种,我们注意到:对于每个点\(u\),和它连边的只有一种点权\(a_u \oplus k\),假设这种点为 \(v\) ,则能和\(v\) 连边的点权为 \(a_v \oplus k=a_u \oplus k \oplus k=a_u\) ,这意味着,对于每两个点 \(x\) , \(y\) , 要不是 \(a_x \oplus a_y =k\)则答案为1 就是 \(a_x==a_y\) 且存在 \(z\) 使 \(a_x \oplus k=a_z\) 则答案为 2 (先通过中转到 \(z\) ,再去 \(y\)),否则,答案为-1。

代码

一下子变简单了。

#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e6+10;
int n,q,a[MAXN],k,x,y,times[(1<<20)+10];
int main(){
    scanf("%d %d",&n,&q);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]),times[a[i]]++;//记录每个权值出现次数
    while(q--){
        scanf("%d %d %d",&k,&x,&y);
        if((a[x]^a[y])==0&&times[(a[x]^k)]>0) printf("2\n");//注意,必须要有中转的点才可输出2
        else if((a[x]^a[y]^k)==0) printf("1\n"); //可以直接连边
        else printf("-1\n");//不能到达
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/werner-yin/p/13197211.html