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;
}

 

posted @ 2016-05-27 00:29  fenicnn  阅读(164)  评论(0编辑  收藏  举报