P2420 让我们异或吧
题目描述
异或是一种神奇的运算,大部分人把它总结成不进位加法.
在生活中…xor运算也很常见。比如,对于一个问题的回答,是为1,否为0.那么:
(A是否是男生 )xor( B是否是男生)=A和B是否能够成为情侣
好了,现在我们来制造和处理一些复杂的情况。比如我们将给出一颗树,它很高兴自己有N个结点。树的每条边上有一个权值。我们要进行M次询问,对于每次询问,我们想知道某两点之间的路径上所有边权的异或值。
维护父节点的同时维护抑或值
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #define N 1010100 using namespace std; int n,m,head[N],tot; struct nodE { int to,next,w; } e[N]; void add(int u,int v,int w) { e[++tot].to=v,e[tot].next=head[u],head[u]=tot,e[tot].w=w; } int p[N][26],f[N][26],dep[N]; void dfs(int u,int fa,int w) { dep[u]=dep[fa]+1,f[u][0]=fa,p[u][0]=w; for(int i=1; (1<<i)<=dep[u]; i++) f[u][i]=f[f[u][i-1]][i-1],p[u][i]=p[u][i-1]^p[f[u][i-1]][i-1]; for(int i=head[u]; i; i=e[i].next) { int v=e[i].to; if(v==fa) continue; dfs(v,u,e[i].w); } } int lca(int u,int v) { int an=0; if(dep[u]>=dep[v]) swap(u,v); for(int i=20; i>=0; i--) { if(dep[u]<=dep[v]-(1<<i)) { if(!an) an=p[v][i]; else an^=p[v][i]; v=f[v][i]; } } if(u==v) return an; for(int i=20; i>=0; i--) { if(f[u][i]!=f[v][i]) { if(!an) an=p[v][i]^p[u][i]; else an^=p[v][i],an^=p[u][i]; u=f[u][i],v=f[v][i]; } } if(v==1) return an^p[u][0]; else if(u==1) return an^p[v][0]; return an^p[u][0]^p[v][0]; } int main() { scanf("%d",&n); for(int u,v,w,i=1; i<n; i++) { scanf("%d%d%d",&u,&v,&w); add(u,v,w),add(v,u,w); } dfs(1,0,0); scanf("%d",&m); for(int u,v,i=1; i<=m; i++) { scanf("%d%d",&u,&v); printf("%d\n",lca(u,v)); } return 0; }
普及一下有关抑或的知识:
抑或: $0^0=0,1^1=0,1^0=1,0^1=1$
观察发现只有二进制位相同时,那一位的抑或值才为真
抑或的几个常用性质:
1.交换律:$a\^b=b\^a$ 显然
2.结合律:$a\^b\^c=a\^(b\^c)$ 显然
3.抑或自己 $a\^a=0$ 显然
4.抑或0 $a\^0=a$ 显然
设$dis[u]$为节点1到$u$所经过路径上的抑或值
那么本题要求就是$(dis[u]^dis[lca(u,v)])^(dis[v]^dis[lca(u,v)])$
因为抑或自己为0,而抑或0为自己,那么1到点u再抑或1到点$lca(u,v)$,就相当于点u抑或了$lca(u,v)$
毫无疑问,抑或满足结合律$(dis[u]^dis[v])^(dis[lca(u,v)]^dis[lca(u,v)])$
由性质3,4得出$ans=dis[u]^dis[v]$
极其简短的代码以及神奇的性质:
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #define N 1010100 using namespace std; int n,m,head[N],tot; struct nodE { int to,next,w; } e[N]; void add(int u,int v,int w) { e[++tot].to=v,e[tot].next=head[u],head[u]=tot,e[tot].w=w; } int dis[N]; void dfs(int u,int fa,int Xor){ dis[u]=Xor; for(int i=head[u];i;i=e[i].next){ int v=e[i].to; if(v!=fa) dfs(v,u,Xor^e[i].w); } } int main() { scanf("%d",&n); for(int u,v,w,i=1; i<n; i++) { scanf("%d%d%d",&u,&v,&w); add(u,v,w),add(v,u,w); } dfs(1,1,1); scanf("%d",&m); for(int u,v,i=1; i<=m; i++) { scanf("%d%d",&u,&v); printf("%d\n",dis[u]^dis[v]); } return 0; }