Description
LF是毒瘤出题人中AK IOI2019,不屑于参加NOI的唯一的人。他对人说话,总是满口垃圾题目者也,教人半懂不懂的。因为他姓李,别人便从QQ群上的“毒瘤李Fee”这半懂不懂的话里,替他取下一个绰号,叫做李Fee。
李Fee一到机房,所有做题的人便都看着他笑,有的叫道,“李Fee,你又来出毒瘤题了!”他不回答,对验题人说,“我又出了两道题,给我验验。”便排出一排毒瘤题。大家又故意的高声嚷道,“你又暴露奸商本性拿毒瘤题骗钱剥削验题人了!”李Fee睁大眼睛说,“你怎么这样凭空污人清白……”“什么清白?我前天亲眼见你出了道毒瘤骗钱题,被PTY把std吊着打。” 李Fee便涨红了脸,额上的青筋条条绽出,争辩道,“出题人的题不能算骗……毒瘤!……出题人的题,能算毒瘤骗钱题么?”接连便是难懂的话,什么“多叉splay随机点分治”,什么“树链剖分套分治FFT”之类,引得众人都哄笑起来:机房内外充满了快活的空气。
虽然他的题十分毒瘤,但他的题还总是有买家。李Fee现在有N道毒瘤题,想将这些题出成一组题来骗大钱。然而显而易见的是,一组题的毒瘤程度不仅和每道题的毒瘤程度有关,也跟它们的排列顺序有关,李Fee需要将它们排列成最毒瘤但又最能骗钱的那个顺序。
具体来说,这N道题每题都有一个毒瘤值,它们构成了一个序列。李Fee心目中有一个理想的毒瘤值序列,这个序列并不一定每一题的毒瘤值都是原本N道题中出现的,所以李Fee准备进行一些改动。这些改动体现在毒瘤值上就是将某道题的毒瘤值改为所有题的毒瘤值的二进制异或值。但是,改动题目是很麻烦的,他想算出最少需要多少次改动才能将原本的毒瘤值序列改成理想的毒瘤值序列,李Fee忙于出毒瘤题,他想请发明O(1/n)算法用暴力搜过所有毒瘤题的你帮他算出答案。但是他是个奸商,所以他并不打算给你报酬。
李Fee一到机房,所有做题的人便都看着他笑,有的叫道,“李Fee,你又来出毒瘤题了!”他不回答,对验题人说,“我又出了两道题,给我验验。”便排出一排毒瘤题。大家又故意的高声嚷道,“你又暴露奸商本性拿毒瘤题骗钱剥削验题人了!”李Fee睁大眼睛说,“你怎么这样凭空污人清白……”“什么清白?我前天亲眼见你出了道毒瘤骗钱题,被PTY把std吊着打。” 李Fee便涨红了脸,额上的青筋条条绽出,争辩道,“出题人的题不能算骗……毒瘤!……出题人的题,能算毒瘤骗钱题么?”接连便是难懂的话,什么“多叉splay随机点分治”,什么“树链剖分套分治FFT”之类,引得众人都哄笑起来:机房内外充满了快活的空气。
虽然他的题十分毒瘤,但他的题还总是有买家。李Fee现在有N道毒瘤题,想将这些题出成一组题来骗大钱。然而显而易见的是,一组题的毒瘤程度不仅和每道题的毒瘤程度有关,也跟它们的排列顺序有关,李Fee需要将它们排列成最毒瘤但又最能骗钱的那个顺序。
具体来说,这N道题每题都有一个毒瘤值,它们构成了一个序列。李Fee心目中有一个理想的毒瘤值序列,这个序列并不一定每一题的毒瘤值都是原本N道题中出现的,所以李Fee准备进行一些改动。这些改动体现在毒瘤值上就是将某道题的毒瘤值改为所有题的毒瘤值的二进制异或值。但是,改动题目是很麻烦的,他想算出最少需要多少次改动才能将原本的毒瘤值序列改成理想的毒瘤值序列,李Fee忙于出毒瘤题,他想请发明O(1/n)算法用暴力搜过所有毒瘤题的你帮他算出答案。但是他是个奸商,所以他并不打算给你报酬。
Input
第一行1个正整数N,如题目所示。
第二行N个非负整数,表示初始的题目毒瘤值序列
第三行N个非负整数,表示理想的题目毒瘤值序列
第二行N个非负整数,表示初始的题目毒瘤值序列
第三行N个非负整数,表示理想的题目毒瘤值序列
Output
单独一行,一个整数,表示最少需要多少次改动
如果怎么改动都无法改成理想的毒瘤值序列,说明这组题出的相当失败,请输出-1
如果怎么改动都无法改成理想的毒瘤值序列,说明这组题出的相当失败,请输出-1
Sample Input
3 0 1 2 3 1 0
Sample Output
2 样例解释: 第一次,整个序列异或为3,把第一个数0换成3,序列变成3,1,2 第二次,整个序列异或为0,把第三个数2换成0,序列变成3,1,0
Data Constraint
对于10%的数据,1<=N<=5
对于30%的数据,1<=N<=10
另有20%的数据,毒瘤值为0或1
对于100%的数据,1<=N<=100000 毒瘤值<2^30
对于30%的数据,1<=N<=10
另有20%的数据,毒瘤值为0或1
对于100%的数据,1<=N<=100000 毒瘤值<2^30
题解
- 首先,我们先来看看对于每一次操作的真实意义是什么
- 假设现在是将y替换所有值的异或和x,那么y的位置就变成的x,这很显然
- 那x变成了什么呢?首先,对于一个数异或两次就等于没做,然后最后异或回来的值还要再异或一次x
- 所以,z=x^y^x=x^x^y=0^y=y
- 那么每次操作就是等于将两个数交换,问题就转换为了给n+1个数,每次可以将两两交换位置,求到目标串的操作次数
- 先来点简单的,考虑-1的情况
- 因为是每次只是位置互换,那么两个字串所拥有的数的集合应该是一样的
- 反则,如果不一样就是-1
- 然后又想到,只是要修改a[i]!=b[i]的位置,那么可以先对a[i]和b[i]离散化,a[i]与b[i]连一条边
- 那么一个联通块都换完的操作次数其实就是联通块中边的个数,因为一条边是当前位置上a[i]与b[i]不相同,那么如果要使它到达目标状态,肯定要操作一次嘛
- 而且再联通块之间跳需要1的代价,因为n+1也就是异或和不一定是存在于当前要处理联通块中,然后要把n+1转移进去
- 如果n+1,在一个联通块中的话就不用考虑,否则单独成为一个联通块
- 最后ans要-1,因为n+1是不用操作的,那么操作数可以-1,因为在做的时候是默认有n+1个做
- 这样的话,最后的答案其实就等于:边数+联通块数-1
- 找联通块数的方法是每次可以从一个没有被访问过的一个联通块的点,dfs跑完整个联通块,这样的话每个联通块只会被做一遍
- 离散化的话可以直接用map
- 最后再来一句,果然是题如其名,这题是真的毒瘤
代码
1 #include <cstdio> 2 #include <iostream> 3 #include <cstring> 4 #include <algorithm> 5 #include <vector> 6 #include <map> 7 #define N 100010 8 using namespace std; 9 vector<int>Q[N]; 10 map<int,int>d; 11 bool vis[N]; 12 int n,a[N],b[N],x[N],y[N],cnt,ans; 13 void dfs(int x) 14 { 15 vis[x]=1; 16 for (int i=0;i<Q[x].size();i++) if (!vis[Q[x][i]]) dfs(Q[x][i]); 17 } 18 int main() 19 { 20 //freopen("duliu.in","r",stdin),freopen("duliu.out","w",stdout); 21 scanf("%d",&n); 22 for (int i=1;i<=n;i++) 23 { 24 scanf("%d",&a[i]),a[n+1]^=a[i]; 25 if (!d[a[i]]) d[a[i]]=++cnt; 26 } 27 if (!d[a[n+1]]) d[a[n+1]]=++cnt; 28 for (int i=1;i<=n;i++) 29 { 30 scanf("%d",&b[i]),b[n+1]^=b[i]; 31 if (!d[b[i]]) d[b[i]]=++cnt; 32 } 33 if (!d[b[n+1]]) d[b[n+1]]=++cnt; 34 memcpy(x,a,sizeof x),memcpy(y,b,sizeof y); 35 sort(x+1,x+n+2),sort(y+1,y+n+2); 36 for (int i=1;i<=n;i++) if (x[i]!=y[i]) { printf("-1"); return 0; } 37 for (int i=1;i<=n+1;i++) a[i]=d[a[i]],b[i]=d[b[i]]; 38 for (int i=1;i<=n;i++) if (a[i]!=b[i]) ans++,Q[a[i]].push_back(b[i]); 39 if (a[n+1]!=b[n+1]) Q[a[n+1]].push_back(b[n+1]); 40 for (int i=1;i<cnt;i++) if (Q[i].size()&&!vis[i]) dfs(i),ans++; 41 if (!vis[cnt]) ans++; 42 printf("%d",ans-1); 43 }