HDOJ 3949 XOR (高斯消元 + XOR线性基)
2016-02-17 20:21
281 查看
点击打开链接
题意:给出n个数,任选大于等于1个数进行异或,得到的结果加入一个集合,问这个集合中第k大的数是多少?
异或类比加减,把n个数的二进制从上到下排成一个矩阵,化成行最简式(每一行的第一个1所在的列上只有这一个1)。那么非零的行所对应的数,是原来那n个数的极大无关组。极大无关组的定义是:线性无关和原来的数都可以由极大无关组中的数来表示。行最简式显然线性无关,并且原来这么得到行最简式的现在就可以怎么回去,一定是可以表示出原来那些数的。化成行最简式的伪代码如下:
for i = 所有数
{
p = i中1所在的最高位
for j = 所有数
if i != j && j中1所在的最高位也为p
a[j] = a[j] ^ a[i];
}
其中内层a[j]改了, 外层循环时也会改变。
为了便于把都是0的行给去掉,一开始我们不要把数拆分成二进制,而是预处理一个数组,数组的第k个数表示2^k次方。倒着循环,如果i大于等于的第一个 ak,那么最高位就在第k个位置。是不是在p,用&,根据与运算的特点考虑。如果不用&,就会TLE。
把a从大到小排序,去掉0后,如果还有num个数,num<n,表示原来的数可以异或得到0,因为有重复的。这里需要特判。所以第k大的数相对来说就是第k-1大的数了。
把k拆分成二进制,把是1的位对应的a累积异或。这个是找规律的,为了不越异或越小,所以之前化成行最简式,把最高位的1都错开。
题意:给出n个数,任选大于等于1个数进行异或,得到的结果加入一个集合,问这个集合中第k大的数是多少?
异或类比加减,把n个数的二进制从上到下排成一个矩阵,化成行最简式(每一行的第一个1所在的列上只有这一个1)。那么非零的行所对应的数,是原来那n个数的极大无关组。极大无关组的定义是:线性无关和原来的数都可以由极大无关组中的数来表示。行最简式显然线性无关,并且原来这么得到行最简式的现在就可以怎么回去,一定是可以表示出原来那些数的。化成行最简式的伪代码如下:
for i = 所有数
{
p = i中1所在的最高位
for j = 所有数
if i != j && j中1所在的最高位也为p
a[j] = a[j] ^ a[i];
}
其中内层a[j]改了, 外层循环时也会改变。
为了便于把都是0的行给去掉,一开始我们不要把数拆分成二进制,而是预处理一个数组,数组的第k个数表示2^k次方。倒着循环,如果i大于等于的第一个 ak,那么最高位就在第k个位置。是不是在p,用&,根据与运算的特点考虑。如果不用&,就会TLE。
把a从大到小排序,去掉0后,如果还有num个数,num<n,表示原来的数可以异或得到0,因为有重复的。这里需要特判。所以第k大的数相对来说就是第k-1大的数了。
把k拆分成二进制,把是1的位对应的a累积异或。这个是找规律的,为了不越异或越小,所以之前化成行最简式,把最高位的1都错开。
#include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #define N 10005 typedef unsigned long long LL; using namespace std; int T, n, q, k, cnt, tmp, t, num; LL sum, s; LL a , p ; void Gauss() { for (int i = 1; i <= n; i++) { for (int j = 63; j >= 0; j--) if (a[i]&p[j]) { tmp = j; break; } for (int j = 1; j <= n; j++) if (j != i) { if (a[j] & p[tmp]) a[j] = a[j] ^ a[i]; } } sort(a+1, a+n+1); t = 1; while (a[t] == 0 && t <= n) t++; num = n-t+1; } int main() { scanf("%d", &T); p[0] = 1; for (int i = 1; i <= 63; i++) p[i] = 2 * p[i-1]; int w = 0; while (T--) { w++; printf("Case #%d:\n", w); scanf("%d", &n); for (int i = 1; i <= n; i++) scanf("%llu", &a[i]); Gauss(); scanf("%d", &q); for (int i = 1; i <= q; i++) { scanf("%d", &k); if (num < n) k--; if (k > p[num]-1 ) { printf("-1\n"); continue; } cnt = 0; sum = 0; while (k > 0) { cnt++; if (k & 1) sum = sum ^ a[cnt+t-1]; k = k / 2; } printf("%llu\n", sum); } } return 0; }
相关文章推荐
- SpringMVC Controller 返回值的可选类型
- Map遍历方法及Map遍历时陷阱
- oracle优化001
- windows多线程同步机制---事件
- 年终奖就这么花掉噜:Apple 苹果 ipad mini 4平板电脑 晒单
- linux umask使用详解
- 阻塞socket和非阻塞socket(一)
- Linux系统用户网络磁盘命令
- HDU5621——数学应用(多边形内对角线交点个数) + 数论 + unsigned long long的应用
- 【iOS】UIPickerView -- 地址选择器:省/市/区
- 7.内存管理
- golang笔记——命令
- CentOS下编译php时的一些典型错误及解决办法
- 学无止境--新浪算法组的博文,都很好
- fragment
- linux命令手册
- vivado sdk生成elf文件出错:make: Interrupt/Exception caught (code = 0xc00000fd, addr = 0x4227d3)
- js判断字符串中出现次数最多的字符 并统计出现次数
- 流媒体平台框架开源EasyDarwin
- 《第五项修炼》阅读笔记