前言
当初思路
开始没想到异或这么多的性质,于是认为对于每个点(u),可以和它连边的点(v)的点权 (a_v=a_u oplus k)(证明:(ecause) (a_uoplus a_v =k) ( herefore) (a_uoplus a_v oplus a_u=a_u oplus k) 即(a_v=a_u oplus k)),于是每次将同一个点权的点放在一起,跑一遍dijkstra
,然后超时了……(赛后重新提交,显示case通过率为32.00%
)
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
#include <queue>
using namespace std;
const int MAXN=1e6+10,MAXLIMIT=1048577;
int n,q,a[MAXN],ans,k,x,y;
std::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
",ans);
else printf("-1
");
}
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 <iostream>
#include <cstdio>
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
");//注意,必须要有中转的点才可输出2
else if( (a[x]^a[y]^k)==0 ) printf("1
"); //可以直接连边
else printf("-1
");//不能到达
}
return 0;
}