1. 程式人生 > 實用技巧 >HDU-4810 Wall Paiting 位運算 , 組合

HDU-4810 Wall Paiting 位運算 , 組合

HDU-4810 Wall Paiting

題意

給定\(n\) 個數,分別輸出\(i\) 個答案,每個答案表示從\(n\) 個數中選擇\(C_n^i\) 組數,計算這組數的異或和,再將這\(C_n^i\) 組數的和相加。

分析

由於是異或,各位之間不相互影響,我們考慮每個數每一位對答案的貢獻,考慮二進位制的第\(i\) 位,如果第\(i\) 位對答案有貢獻,也就是\(1\) ,那麼這一位一定是由奇數個1異或得到,所以我們其實只要統計第\(i\) 位為\(1\) 的個數,對這個排列組合即可。注意還要乘上\(2^i\)

程式碼

ll C[1005][1005];
int num[35];
void get_C()
{
    C[0][0] = 1;
    for (int i = 1; i <= 1003; i++)
    {
        C[i][0] = 1;
        for (int j = 1; j <= i; j++)
            C[i][j] = C[i - 1][j] + C[i - 1][j - 1], C[i][j] %= MOD;
    }
}

int main() {
    int n;
    get_C();
    while (~scanf("%d", &n)) {
        memset(num, 0, sizeof num);
        for (int i = 0; i < n; i++) {
            ll x = readint();
            for (int j = 32; j >= 0; j--)
                if ((1ll << j) & x) num[j]++;
        }
        for (int i = 1; i <= n; i++) {
            ll sum = 0;
            for (int j = 32; j >= 0; j--) {
                ll res = 0;
                for (int k = 1; k <= i; k += 2) {
                    res += C[num[j]][k] * C[n - num[j]][i - k] % MOD;
                    res %= MOD;
                }
                sum = (ksm(2, (ll)j, MOD) * res % MOD + sum) % MOD;
            }
            Put(sum);
            putchar(i == n ? '\n' : ' ');
        }
    }
}