멤브레인 레코드 (Niuke Xiaobaiyue 일치 23A, 하위 집합 접두사 합계)

1. 제목 링크 :

막 기록

2. 주제의 주요 아이디어 :

중국어 질문 ~~

3. 분석 :

n은 20에 불과하므로 이진 열거 연산의 행을 고려하십시오.

따라서 cnt [i]로 표시되는 행에서 i 연산을 수행 한 후 0 개의 열 수만 전처리하면됩니다.

먼저 열 상태가 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 후 어떤 열이 0 열이 될 것인지 고려할 때 m-zero 열의 수는 여전히 필요한 열 피연산자의 수입니다.

행 연산 1100 이후 열 상태가 0000, 0100, 1000, 1100 인 열은 모두 0 열이된다는 것을 쉽게 알 수 있습니다.

따라서 행 연산 (1100) 이후 0 열의 수는 cnt2 [0000] + cnt2 [0100] + cnt2 [1000] + cnt2 [1100]입니다.

즉, 행 연산 i 후 0 개 열의 수는입니다  \ sum_ {j} cnt2 [j]. 여기서 j는 i의 하위 집합입니다.

부분 집합 접두사 합계

4. 코드 구현 :

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

 

추천

출처blog.csdn.net/The___Flash/article/details/105026057