【整理】XOR:从陌生到头晕
一:解决XOR常用的方法:
在vjudge上面输入关键词xor,然后按照顺序刷了一些题。
然后大概悟出了一些的的套路:
- 常用的有贪心,主要是利用二进制的一些性质,即贪心最大值的尽量高位取1。
- 然后有前缀异或和,和普通前缀和一样,可以快速得到一段区间的异或和。
- 当然在一颗树里面也常用前缀异或和,得到根节点到每个节点的前缀异或和,然后,两个点的前缀异或和在异或,可以得到两个点之间路线的异或和。 因为LCA到根的公共部分可以抵消(感谢Lzh提醒)。
- Trie树,可以快速在数组里找自己的最大异或。
- 这种题,当然少不了分块,随机应变吧。
-------------------------------我是分界线-----------------------------------
-
POJ 3764: The xor-longest 字典树求最大异或
题意:给出一棵树,在树上找出一条路径,使得路径伤的边的异或值最大。
思路:dfs得到根到节点的异或前缀和,然后把每个点的异或前缀和插入字典树中,就可以按套路,在字典树里找最大异或了。
#include<cmath> #include<cstdio> #include<cstring> #include<cstdlib> #include<iostream> #include<algorithm> using namespace std; const int maxm=200010; const int maxn=3000000; int Laxt[maxm],Next[maxm],To[maxm],val[maxm],cnt,Xor[maxm];//dfs int ch[maxn][2],tot,ans,b[40],n;//trie int q_pow(int a,int x){ int res=1;while(x){if(x&1) res*=a;x>>=1;a*=a;} return res;} int read() { int res=0; char c=getchar(); for(;c>'9'||c<'0';c=getchar()); for(;c<='9'&&c>='0';res=res*10+c-'0',c=getchar()) ; return res; } void init() { memset(Laxt,0,sizeof(Laxt)); memset(Xor,0,sizeof(Xor)); memset(ch,0,sizeof(ch)); cnt=tot=ans=0; } void add(int u,int v,int x) { Next[++cnt]=Laxt[u]; Laxt[u]=cnt; To[cnt]=v; val[cnt]=x; } void dfs(int u,int pre,int x) { for(int i=Laxt[u];i;i=Next[i]){ if(To[i]!=pre){ Xor[To[i]]=x^val[i]; dfs(To[i],u,Xor[To[i]]); } } } void insert(int x) { int Now=0; for(int i=0;i<=31;i++) { b[i]=x&1;x>>=1;} for(int i=31;i>=0;i--){ if(!ch[Now][b[i]]) ch[Now][b[i]]=++tot; Now=ch[Now][b[i]]; } } void find(int x) { int Now=0,tmp=0; for(int i=0;i<=31;i++){ b[i]=x&1; x>>=1; } for(int i=31;i>=0;i--){ if(ch[Now][b[i]^1]) Now=ch[Now][b[i]^1],tmp+=q_pow(2,i); else Now=ch[Now][b[i]]; } ans=max(ans,tmp); } void build() { for(int i=1;i<=n;i++) insert(Xor[i]); for(int i=1;i<=n;i++) find(Xor[i]); } int main() { while(~scanf("%d",&n)){ init(); int u,v,x; for(int i=1;i<n;i++){ u=read();v=read();x=read(); u++;v++; add(u,v,x); add(v,u,x); } dfs(1,0,0); build(); printf("%d\n",ans); } return 0; }
-
SPOJ maxxor:Find the max XOR value 贪心 (下面是错误的)
题意: 给定L,R。求L<=X1,X2,X3...<=R,使得X1^X2^X3...最大异或。
思路: 可以选的数大于大于两个,先求最大的n,使得2^n<=R, 如果还可以异或一个,那么异或(2^n)-1就好了。(2^n)xor(2^n-1) =2^(n+1)-1。
一定是最大的。比如2^=10000, n=4,10000 xor 01111 = 11111;不可能还有不这个大的了,毕竟n=4是上界; 当然只能选一个的时候,
就选本身R就好了。当然,为了避免卡精度问题(比如CF就hack我了,mmp),pow函数最好比较一下,这里太懒,算了。
#include<cstdio> #include<cstdlib> #include<iostream> #include<algorithm> #include<cmath> using namespace std; int main() { int a,b,ans,L,R; while(~scanf("%d%d",&L,&R)){ if(L==R) printf("%d\n",L); else { int a=log2(R); a=q_pow(2,a); printf("%d\n",a+a-1); } } return 0; }
-
HDU 4825:Xor Sum 字典树
题意: 给出n个数a[],然后给出m次问题,给出Y,求a[]里面的X,使得X xor Y最大。
思路: 如果找一个数的最大异或,当然我们需要从高位到低位(已转化为二进制),尽可能不同。
那么我们从高位到低位表示一个数X,并且存入字典树中,结尾节点记录X(不同的数结尾肯定不同)。
对于询问,从高位到地位先尽可能从兄弟边走。
这棵Trie树高度为32,复杂度在接受范围内。
#include<cstdio> #include<cstdlib> #include<iostream> #include<cmath> #include<algorithm> #include<cstring> using namespace std; const int maxn=3000010; inline int read() { int res=0;char x=getchar();while(x<'0'||x>'9') x=getchar(); while(x>='0'&&x<='9'){ res=res*10+x-'0';x=getchar();}return res; } int q_pow(int a,int x){ int res=1;while(x){ if(x&1) res*=a; x>>=1; a=a*a;} return res; } struct TREE { int ch[maxn][2],cnt,num[maxn]; void init() { memset(ch,0,sizeof(ch));cnt=0;} void insert(int s[],int val) { int Now=0; for(int i=31;i>=0;i--){ if(!ch[Now][s[i]]) ch[Now][s[i]]=++cnt; Now=ch[Now][s[i]]; } num[Now]=val; } int query(int s[]) { int Now=0; for(int i=31;i>=0;i--){ if(ch[Now][s[i]^1]) Now=ch[Now][s[i]^1]; else Now=ch[Now][s[i]]; } return num[Now]; } }Tree; int main() { int T,n,m,x,tx,a[32],Case=0; scanf("%d",&T); while(T--){ printf("Case #%d:\n",++Case); scanf("%d%d",&n,&m); Tree.init(); for(int i=1;i<=n;i++){ scanf("%d",&x);tx=x; for(int j=0;j<=31;j++) { a[j]=x%2; x>>=1; } Tree.insert(a,tx); } for(int i=1;i<=m;i++) { scanf("%d",&x); for(int j=0;j<=31;j++) { a[j]=x%2; x>>=1; } printf("%d\n",Tree.query(a)); } }return 0; }
-
NBUT 1597:Find MaxXorSum 字典树
(和上一题差不多,就不说了)
-
CodeForces 617E:XOR and Favorite Number 分块 (前缀异或和)
题意: 给定数列a[],和m个询问 Q(L,R),回答每个询问中有多少对(L<=i<=j<=R) ,使得异或为k。
思路: 异或转化为前缀和处理。然后就差不多交给分块处理了。 分块的时候记录个数,记录区间信息,具体的代码里面去感受。
#include<cstdio> #include<cstdlib> #include<iostream> #include<algorithm> #include<cstring> #include<cmath> #define ll long long using namespace std; const int maxn=100010; int a[maxn],pre[maxn],num[1<<20],n,m,k,sqrtn; struct Query{ int id,l,r; ll ans; }q[maxn]; bool cmp(const Query a,const Query b) { if(a.l/sqrtn==b.l/sqrtn) return a.r<b.r; return a.l<b.l; } bool cmp2(const Query a,const Query b) { return a.id<b.id; } int main() { scanf("%d%d%d",&n,&m,&k); sqrtn=(int)sqrt(n); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); pre[i]=pre[i-1]^a[i]; } for(int i=0;i<m;i++){ scanf("%d%d",&q[i].l,&q[i].r); q[i].id=i; } sort(q,q+m,cmp); int l=1,r=1; num[pre[1]]++;num[0]++; ll cur=(a[1]==k?1:0); for(int i=0;i<m;i++) { while(r<q[i].r){ cur+=num[pre[r+1]^k]; r++; num[pre[r]]++; } while(l<q[i].l){ num[pre[l-1]]--; cur-=num[pre[l-1]^k]; l++; } while(l>q[i].l){ cur+=num[pre[l-2]^k]; num[pre[l-2]]++; l--; } while(r>q[i].r){ num[pre[r]]--; cur-=num[pre[r]^k]; r--; } q[i].ans=cur; } sort(q,q+m,cmp2); for(int i=0;i<m;i++) printf("%lld\n",q[i].ans); return 0; }
-
HDU 5661: Claris and XOR 贪心
题意:现在对对于这个题,求a<=X<=b,c<=Y<=d,使XxorY最大(不同位数最多)。
思路:从高位向地位,能取不同则取不同。
#include<cstdio> #include<cstdlib> #include<iostream> #include<cmath> using namespace std; long long a,b,c,d,x,y,t1,t2,ans; int main() { long long i,j,T; scanf("%lld",&T); while(T--){ ans=x=y=0; scanf("%lld%lld%lld%lld",&a,&b,&c,&d); for(i=62;i>=0;i--){ t1=x+(1LL<<i);t2=y+(1LL<<i); if(t1<=b&&t2-1>=c) { x=t1;ans+=(1LL<<i); } else if(t2<=d&&t1-1>=a){ y=t2;ans+=(1LL<<i); } else if(t1-1>=a&&t2-1>=c){ continue; } else if(t1<=b&&t2<=d){ x=t1;y=t2; } } printf("%lld\n",ans); } return 0; }
-
ZOJ 3432: Find the Lost Sock 异或的效果
问题: 有2*n-1个袜子,叫你找出不能配对的那个袜子。
#include<cstdio> #include<cstdlib> #include<iostream> #include<cstring> #include<memory.h> using namespace std; char s[10],c; int main() { int n; while(~scanf("%d\n",&n)){ for(int i=0;i<=7;i++) s[i]='\0'; for(int i=1;i<2*n;i++){ for(int j=0;j<8;j++){ c=getchar(); s[j]=s[j]^c; } } printf("%s",s); } return 0; }
-
NBUT1615:XorXor 异或的性质
问题:求所有区间异或和的异或和。
思路:由于异或满足交换律,偶数次异或的数字可以怼掉。所以只需要求出每个数字是出现奇数次还是偶数次即可。
#include<cmath> #include<cstdio> #include<cstdlib> #include<cstring> #include<iostream> #include<algorithm> using namespace std; int main() { int T,n,i,x,ans; scanf("%d",&T); while(T--){ scanf("%d",&n); ans=0; for(i=1;i<=n;i++){ scanf("%d",&x); if((i*(n+1-i))&1) ans^=x; } printf("%d\n",ans); } return 0; }
-
CodrChef:Bear and Xor of Sums
题意:问所有的区间和的异或和。
思路:和上一题不同,这里有加法。
It is your time to fight!