foj 2044 1 M possible 二进制压缩
题目链接:
http://acm.fzu.edu.cn/problem.php?pid=2044
题意:
给出 一大堆数,找出2个出现次数模3 为1 的两个数字
题解:
把一个数分为几位拆开统计,统计完后,把所有的位数都模三,这样剩下的数就为a和b的叠加了,但是信息丢失太多了,没有办法把a,b区分开。
所以我们要多一个维护,在统计一个数对每一位的贡献的时候,同时统计任意两个位的联系的贡献(mp[i][j]++),这样最后把mp所有记录的联系都mod3剩下的就是a,b各自的联系了,这样就区分开了。
#include<iostream> #include<cstdio> #include<cstring> using namespace std; int mp[32][32]; int cnt[32],pos[32],tot; int main() { int tc,n,x,a,b,i,j,k,t; scanf("%d", &tc); while (tc--) { memset(cnt, 0, sizeof(cnt)); memset(mp, 0, sizeof(mp)); scanf("%d", &n); for (i = 0; i < n; i++) { scanf("%d", &x); tot = 0; for (j = 0; j < 31; j++) { if (x&(1 << j)) { pos[tot++] = j; cnt[j]++; } } for (j = 0; j < tot; j++) { for (k = j + 1; k < tot; k++) { mp[pos[j]][pos[k]]++; mp[pos[k]][pos[j]]++; } } } for (i = 0; i < 31; i++) cnt[i] %= 3; for (i = 0; i < 31; i++) { for (j = 0; j < 31; j++) { mp[i][j] %= 3; } } a = 0; for (i = 0; i < 31; i++) { if (cnt[i]) { cnt[i]--; a ^= (1 << i); for (j = 0; j < 31; j++) { if (mp[i][j]) { mp[i][j]--; mp[j][i]--; cnt[j]--; a ^= (1 << j); } } break; } } b = 0; for (i = 0; i < 31; i++) { if (cnt[i]) b ^= (1 << i); } if (a > b) { t = a; a = b; b = t; } printf("%d %d\n", a, b); } return 0; }