前言
开始没想到异或这么多的性质,于是认为对于每个点\(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&×[(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;
}