膜法記錄(牛客小白月賽23 A,子集前綴和)

一.題目鏈接:

膜法記錄

二.題目大意:

中文題~~

三.分析:

由於 n 只有 20,考慮二進制枚舉操作的行

因此我們只需預處理出對行進行 i 操作後,零列的個數,記爲 cnt[i].

先求出列狀態爲 i 的列的個數,記爲 cnt2[i] 中.

那麼 cnt[i] = sum(cnt2[i 的子集]).

例如求出列狀態數 cnt2[00], cnt2[01], cnt2[10], cnt2[11] 後,那麼

cnt[00] = cnt2[00]

cnt[01] = cnt2[00] + cnt2[01]

cnt[10] = cnt2[00] + cnt2[10]

cnt[11] = cnt2[00] + cnt2[01] + cnt2[10] + cnt2[11].

很明顯求出 cnt2[] 後,再求子集前綴和即可.

比如輸入爲:

1
4 4 2 3
*...
*...
.***
..**

那麼列狀態分別爲 0011、0100、1100、1100

立即推 cnt2[0011] = 1, cnt2[0100] = 1, cnt2[1100] = 2.

假設對行 3,4 進行操作,那麼行操作狀態爲 1100.

考慮進行行操作 1100 後,哪些列會變爲零列,那麼 m - 零列個數 便爲還需的列操作數.

易知進行行操作 1100 後,列狀態爲 0000、0100、1000、1100 的列都會變爲零列.

因此進行行操作 1100 後,零列個數便爲 cnt2[0000] + cnt2[0100] + cnt2[1000] + cnt2[1100].

即進行行操作 i 後,零列個數便爲 \sum_{j}cnt2[j],其中 j 爲 i 的子集.

子集前綴和

四.代碼實現:

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;

const int M = (int)1e5;
const int N = (int)2e1;

char s[N + 5][M + 5];
int cnt[1<<N];

int main()
{
    int T; scanf("%d", &T);
    while(T--)
    {
        int n, m, a, b; scanf("%d %d %d %d", &n, &m, &a, &b);
        for(int i = 0; i < (1<<n); ++i) cnt[i] = 0;
        for(int i = 0; i < n; ++i)  scanf("%s", s[i]);
        for(int i = 0; i < m; ++i)
        {
            int state = 0;
            for(int j = 0; j < n; ++j)  state |= (s[j][i] == '*' ? (1<<j) : 0);
            ++cnt[state];
        }
        for(int i = 0; i < n; ++i)//子集前綴和
        {
            for(int j = 0; j < (1<<n); ++j) if(!(j & (1<<i)))   cnt[j | (1<<i)] += cnt[j];
        }
        bool flag = 0; for(int i = 0; i < (1<<n); ++i)  flag |= (__builtin_popcount(i) <= a && m - cnt[i] <= b);
        puts(flag ? "yes" : "no");
    }
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章