hdu3949
来源:互联网 发布:淘宝客导购网站 编辑:程序博客网 时间:2024/05/29 15:16
XOR
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 1841 Accepted Submission(s): 586
For each test case, the first line is an integer N(1<=N<=10000), the number of numbers below. The second line contains N integers (each number is between 1 and 10^18). The third line is a number Q(1<=Q<=10000), the number of queries. The fourth line contains Q numbers(each number is between 1 and 10^18) K1,K2,......KQ.
221 241 2 3 431 2 351 2 3 4 5
Case #1:123-1Case #2:0123-1HintIf you choose a single number, the result you get is the number you choose.Using long long instead of int because of the result may exceed 2^31-1.
题意:从n个数中任选m(m>=1)个数异或起来,求所有可能的异或和的第k小值。
这题传说是什么线性基之类的,但实际并不完全是基,因为基的概念是两两内积为0.
下面说题解:
简单来说,我有a和b两个数,假设(a,b)是a和b能够异或出来的所有值,当然这里(a,b)={a,b,a^b},可能要去重,其实我们不用关注具体是多少,只要知道用(a,b)表示就行了,那么我们有结论(a,b)=(a^b,b)=(a^b,a)=(a,b,a^b)等等,同时还有交换律(a,b)=(b,a),我整一个简单的吧(a,b)=(a^b,b),证明两个集合相等,显然是证互相包含了,首先a和b可以变出a^b,这样我们就可以利用a^b和b来生成其他数,于是(a,b)是包含(a^b,b)的,同理a^b和b可以先变出a^b^b=a,于是(a^b,b)包含(a,b),因此有(a,b)=(a^b,b),其他同理可证.
这里做推广到(a1,a2,a3,....,an),比如b1=a1^a2,那么保留至少一个a1或a2,我们有(b1,a2,a3,...,an)=(a1,a2,a3,...,an),以此我们可以一直做下去...甚至完全变成(b1,b2,b3,...,bn).这是个什么过程呢?很明显高斯消元吗.
既然(a1,a2,a3,...,an)=(b1,b2,b3,...,bn),那么我们想方设法让b1,b2,b3,...,bn尽量变成基,也就是理想情况下两两内积为0.我们考虑将a1,...,an写成二进制形式,而异或等价于模2加法。
于是a1,..,an所能表示的所有数的形式为c=(a1*x1)^(a2*x2)^....^(an*xn),x1,x2,...xn取{0,1},xi表示ai这个数取不取,我们按位考虑,因为所有ai最多60位,因此c[i]=(a1[i]*x1+a2[i]*x2+...+an[i]*xn)%2; c[i]表示c的二进制表示第i位,ak[i]同理.
这样我们也成矩阵形式
(a1[0],a2[0],...,an[0]) (x[1]) (c[0])
(a1[1],a2[1],...,an[1]) (x[2]) (c[1])
(................................) (*****) %2= (*****)
(.....................................) (*****) (*****)
(a1[60],a2[60],...,an[60]) (x[n]) (c[60])
实际并不是这么做的,我们要将矩阵转置过来
(a1[0],a1[1],...,a1[60]) (a1[0]) (a2[0]) (an[0]) (%2) (c[0])
(a2[0],a2[1],...,a2[60]) (a1[1]) (a2[1]) (an[1]) (%2) (c[1])
(.................................) 然后写成这种形式x1 (........) + x2 (........) + xn (........) (......) = (......)
(.................................) (........) (.........) (........) (......) (......)
(an[0],an[1],...,an[60]) (a1[60]) (a2[60]) (an[60]) (%2) (c[n])
这样基的形式就很明显,于是我们对转置矩阵进行高斯消元,理想情况是消成对角型矩阵。
(1,0,0,0,0)
(0,1,0,0,0)
(0,0,1,0,0)
(0,0,0,1,0)
(0,0,0,0,1)
这样是真正的两两内积为0,这样当所有xi遍历{0,1},将得到2^n-1种结果,-1是因为所有xi不能同时取0,因为题目条件限制,必须取m(m>=1)个数异或.不理想情况呢?
(1,0,0,0,0)
(0,1,0,0,1)
(0,0,1,0,0)
(0,0,0,1,1)
列数多的情况(1,0,1,0,0)
(0,1,1,0,1)
(0,0,0,1,1)
(0,0,0,0,0)
消不成对角型的情况这些列可能会有多个1,但是实际并不影响
比如我们看这个矩阵
(1,0,1,0,0)
(0,1,1,0,1)
(0,0,0,1,1)
(0,0,0,0,0)
我们看前两列(从左到右是高位->低位),可以组成(11),(10),(01),(00),第1位为1对应了x1=1,第2位1对应了x2=1,他们之所以可以取遍2^2=4种可能,是因为他们是基,基是独立的,那么第3位呢?对角上没有1啊,这样其实第3位的值是取决于前2位的,也就是固定的,比如(11)第三位必然是0,因为(1,1)代表x1=x2=1,也就是第1,2行都是取的,由于第1,2行的第3列都是1,因此异或起来为0(这个取决于奇偶性了),于是第3位是确定的,因此可以得到(110),(101),(011),(000),由于第4位又是对角上的1(位置并不一定在对角线,但1所在的行这个1是第一次出现,其实就是一个行阶梯型),显然又是独立的,也就是x3可取0和1
x3=1,得到(1101),(1011),(0111),(0001)
x3=0,得到(1100),(1010),(0110),(0000)
可以发现每遇到对角上一个1,能表示的数就翻倍了。
看第5列,由于是多出来的,对角没有元素了,同样的第5位是前4位决定的,如(1101)表示x1=x2=x3=1,那么第5位就是第2,3行第5列的1异或起来,也就是(11010)。
说了一堆,也就是所能表示的数的个数就是pow(2,行阶梯型的对角1的个数),注意0的问题,如果不能取0,则需-1,能不能取0,取决于最后一行是不是全0,若是的话,则取x1=x2=...=xn-1=0,xn=1,就能得到0,否则得不到0.
说了一堆,看出了类似线性基的样子,并且可以发现从第1列到最后一列的扫描过程,生成的数字是排好序的,恰好可以发现第k大,如果能取0,则k=k-1,那么k的二进制表示刚好对应了线性基的取法,例如k=(1101),则表示取x1=x2=x4=1,x3=0这个对照上面的扫描过程理解一下吧,不详细解释了,也比较难解释清楚!
提交一直WA,各种调试对拍,最后发现一个神奇的BUG,见代码。
代码:
#include<cstdio>#include<iostream>#include<cstring>#include<algorithm>#include<vector>#include<map>#define Maxn 10010#define ll long longusing namespace std;ll a[Maxn];int d;void gauss(int n){ int r=1,i,j; for(i=60;i>=0;i--){ if(!(1LL<<i&a[r])){ for(j=r+1;j<=n;j++) if(1LL<<i&a[j]) break; if(j>n) continue; swap(a[r],a[j]); } for(j=1;j<=n;j++){ if(j==r||!(1LL<<i&a[j])) continue; a[j]^=a[r]; } r++; if(r==n+1) break; //BUG:r=n+1会利用前一组数据,需要break } d=r-1;}ll query(ll x,int n){ x-=d!=n; if(!x) return 0; if(x>=(1LL<<d)) return -1; ll ans=0; for(int i=d-1;i>=0;i--) if(x>>i&1) ans^=a[d-i]; return ans;}int main(){ int t,n,m,cas=1; ll x; cin>>t; while(t--){ cin>>n; for(int i=1;i<=n;i++) scanf("%I64d",a+i); gauss(n); cin>>m; printf("Case #%d:\n",cas++); while(m--){ scanf("%I64d",&x); printf("%I64d\n",query(x,n)); } } return 0;}
- hdu3949
- hdu3949
- HDU3949 XOR
- hdu3949:XOR
- 【HDU3949】XOR
- hdu3949 XOR
- hdu3949-高斯消元法的应用
- 【高斯消元应用】hdu3949
- 【HDU3949】XOR 线性基
- [HDU3949]XOR-线性基
- [线性基] HDU3949: XOR
- HDU3949:XOR 高斯消元求线性基+二进制拆分
- 【HDU3949】XOR——线性基
- [HDU3949]XOR(高斯消元求线性基)
- hdu3949 XOR (线性基(高斯消元))
- BZOJ 2844 异或线性基(HDU3949 升级版
- hdu3949 XOR(求所有的异或和的第k小,高斯消元求线性基)
- 缓存的查询
- javadoc 警告:编码 GBK 的不可映射字符
- struct和typedef struct彻底明白了
- 关于statusLine中的sp,lf,cr
- android 设置系统屏幕亮度
- hdu3949
- Android的Canvas、Bitmap、Drawable和Paint
- PHP下载txt文件到浏览器
- java集合框架总结
- Android中ListView的简单使用
- 前端面试题目搜集
- java play framework安装配置及初步学习
- (二)WebView控件---》应用程序内置浏览器
- oracle exp imp 备份 还原