T1.
题意:
给出一个n个数的序列a,每次操作可以将a中一个数变成整个序列的值的异或。求最少需要多少次才能将a变成目标序列b,无法完成输出-1。
题解:
异或题(位运算题)都不会是纯模拟(其实知道也写不出正解),所以考完多做点这种题练练手感熟悉操作。
一个性质:一个数异或同一个数两次后还是原来的数,证明枚举一下就行。
根据这个性质我们可以看出题其实目就是一个n+1的序列交换顺序,达到期望序列,不然输出-1。交换过程把不相等的a[i]和b[i]连成边,交换次数即联通块边数。跳到不同联通块次数要加1,n+1不同的话单独算一个连通块。不难发现最后答案就是:连通块数+边数-1,因为n+1不算操作,但我们在计算的时候算上了它。
代码:
#include<bits/stdc++.h> #define For(i,l,r) for(int i=l;i<=r;i++) #define ll long long using namespace std; const int M=1e5+5; int n,a[M],b[M],x[M],y[M],vis[M],cnt,ans; map<int,int>d; vector<int>Q[M]; inline int read(){ int f=1,sum=0; char ch=getchar(); while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();} while(isdigit(ch)){sum=(sum<<1)+(sum<<3)+(ch^48);ch=getchar();} return f*sum; } inline void dfs(int x){ vis[x]=1; For(i,0,Q[x].size()-1){ if(!vis[Q[x][i]]){ dfs(Q[x][i]); } } } int main(){ n=read(); For(i,1,n){ a[i]=read();a[n+1]^=a[i]; if(!d[a[i]]) d[a[i]]=++cnt; } if(!d[a[n+1]]) d[a[n+1]]=++cnt; For(i,1,n){ b[i]=read();b[n+1]^=b[i]; if(!d[b[i]]) d[b[i]]=++cnt; } if(!d[b[n+1]]) d[b[n+1]]=++cnt; memcpy(x,a,sizeof(x));memcpy(y,b,sizeof(y)); sort(x+1,x+n+2);sort(y+1,y+n+2); For(i,1,n) if(x[i]!=y[i]){printf("-1");return 0;} For(i,1,n+1) a[i]=d[a[i]],b[i]=d[b[i]]; For(i,1,n) if(a[i]!=b[i]) ans++,Q[a[i]].push_back(b[i]); if(a[n+1]!=b[n+1]) Q[a[n+1]].push_back(b[n+1]); For(i,1,cnt) if(Q[i].size()&&!vis[i]) dfs(i),ans++; printf("%d",ans-1); return 0; }