/*
* Problem :: 3019 / 테트리스
*
* Kind :: Simulation
*
* Insight
* - 블록과 바닥 사이에 채워져있지 않은 칸이 생기면 안된다
* + 블록과 바닥이 접할 때 빈칸이 있으면 안된다
* # 바닥의 모양으 고정되어있으니
* 떨어지는 블록의 가장 아랫부분의 모양에 따라 유효한 방법인지 결정된다
* -> 떨어지는 블록이 몇번째 블록인지와 어디로 떨어지는지
* 그리고 그 블록이 회전함으로써 가능한 블록의 가장 아랫부분의 모양을 고려하며
* 블록을 놓는 서로 다른 방법의 수를 구하자
*
* - 5번 블록을 예로 들어보자
* + ㅁ
* ㅁㅁㅁ
* 모양으로 떨어지는 경우
* 바닥의 3칸 연속 높이가 같아야만 유효한 방법이다
* + ㅁ
* ㅁㅁ
* ㅁ
* 모양으로 떨어지는 경우
* 바닥의 첫번째 칸은 두번째 칸보다 높이가 1 커야만 유효한 방법이다
* + ㅁ
* ㅁㅁ
* ㅁ
* 모양으로 떨어지는 경우
* 바닥의 두번째 칸은 첫번째 칸보다 높이가 1 커야만 유효한 방법이다
* + ㅁㅁㅁ
* ㅁ
* 모양으로 떨어지는 경우
* 바닥의 첫번째 칸과 세번째 칸은 두번째 칸보다 높이가 1 커야만 유효한 방법이다
* # 이는 차례대로 바닥의 모양을
* {0, 0, 0}
* {1, 0}
* {0, 1}
* {1, 0, 1}
* 로 패턴화 시킬 수 있다
* -> 이 패턴을 가지고 블록이 떨어지고 있는 위치에서
* 바닥의 모양이 위 패턴에 부합하는지 확인하면 유효한 방법인지 알 수 있다
*
* - 바닥이 현재
* 2 1 1 1 0 1 이고
* 바닥의 모양이 {1, 0, 1} 의 패턴에 부합해야 한다고 하자
* + 인덱스 0 인 위치에 블록이 떨어질 때,
* 그 위치의 바닥 모양 2 1 1 은 {1, 0, 1} 패턴에 부합하는가?
* (2-1, 1-0, 1-1) => (1, 1, 0) 으로 부합하지 않는다
* # 패턴은 현재 가장 높이가 낮은 칸 기준
* 그 칸을 기준으로 높이가 높은 다른 칸에 양수가 들어가는 방식이므로
* 이 양수를 그 칸의 바닥높이에서 빼줌으로써
* 블록이 바닥에 접하는 가장 낮은 높이를 구할 수 있다
* 여기서 모든 칸이 위 높이와 같을 때 유효한 방법이라는 것을알 수 있다
* 위 원리를 이용해서 하나씩 검사하면 된다
* -> 인덱스 1 인 위치의 바닥 모양 1 1 1 은 {1, 0, 1} 패턴에 부합하는가?
* 인덱스 2 인 위치의 바닥 모양 1 1 0 은 {1, 0, 1} 패턴에 부합하는가?
* 인덱스 3 인 위치의 바닥 모양 1 0 1 은 {1, 0, 1} 패턴에 부합하는가?
*
* Point
* - 사실...
* 그냥 각 경우에 대해 하나하나 코딩하는 것이
* 좀 하드코딩이긴 하지만 확실한 방법이고 더 쉬울 수도 있다
* + 이전에는 하드코딩으로 풀었기에 좀더 효율적인 다른 방식으로 풀고 싶기도 했고
* # 바닥의 모양만 알면 블록의 번호와 상관없이 유효한지 알 수 있기 때문에
* 이번에는 위처럼 풀었다 :)
*/
//
// BOJ
// ver.C++
//
// Created by GGlifer
//
// Open Source
#include <iostream>
#include <vector>
using namespace std;
#define endl '\n'
// Set up : Global Variables
int C, P;
vector<int> G;
// Set up : Functions Declaration
int cases(vector<int> pattern);
int main()
{
// Set up : I/O
ios::sync_with_stdio(false);
cin.tie(nullptr);
// Set up : Input
cin >> C >> P;
G.resize(C);
for (int i=0; i<C; i++)
cin >> G[i];
// Process
int ans = 0;
switch (P) {
case 1: /* 1번 블록 */
/* 1번 블록에서 유효한 바닥 모양의 패턴들 */
ans += cases({0});
ans += cases({0, 0, 0, 0});
break;
case 2: /* 2번 블록 */
/* 2번 블록에서 유효한 바닥 모양의 패턴들 */
ans += cases({0, 0});
break;
case 3: /* 3번 블록 */
/* 3번 블록에서 유효한 바닥 모양의 패턴들 */
ans += cases({0, 0, 1});
ans += cases({1, 0});
break;
case 4: /* 4번 블록 */
/* 4번 블록에서 유효한 바닥 모양의 패턴들 */
ans += cases({1, 0, 0});
ans += cases({0, 1});
break;
case 5: /* 5번 블록 */
/* 5번 블록에서 유효한 바닥 모양의 패턴들 */
ans += cases({0, 0, 0});
ans += cases({1, 0});
ans += cases({0, 1});
ans += cases({1, 0, 1});
break;
case 6: /* 6번 블록 */
/* 6번 블록에서 유효한 바닥 모양의 패턴들 */
ans += cases({0, 0, 0});
ans += cases({0, 0});
ans += cases({0, 1, 1});
ans += cases({2, 0});
break;
case 7: /* 7번 블록 */
/* 7번 블록에서 유효한 바닥 모양의 패턴들 */
ans += cases({0, 0, 0});
ans += cases({0, 0});
ans += cases({1, 1, 0});
ans += cases({0, 2});
break;
default: throw;
}
// Control : Output
cout << ans << endl;
}
// Helper Functions
int cases(vector<int> pattern)
/* 바닥 모양의 패턴이 들어왔을 때 유효한 서로 다른 방법의 수를 반환 */
{
int cnt = 0; /* 유효한 서로 다른 방법의 수 */
int L = pattern.size(); /* 블록과 접하는 바닥의 길이 = 떨어지는 블록의 가로 길이 */
for (int i=0; i<=C-L; i++) { /* 인덱스 i 인 위치에 블록이 떨어질 때 */
/* 편의상 고려할 G[i] ~ G[i+L-1] 의 바닥을 sub 라는 Vector 로 만듦 */
vector<int> sub(G.begin()+i, G.begin()+i+L);
/* 첫번째 칸의 패턴과 바닥의 높이를 통해 블록이 바닥에 접하는 가장 낮은 높이를 구함 */
int h = sub[0] - pattern[0];
if (h < 0) continue; /* 높이는 음수일 수 없음 */
bool isValid = true; /* 현재 떨어지는 방식이 유효한지 여부 */
for (int j=1; j<L; j++) { /* 두번째 칸부터 차례로 높이 검사 */
/* 현재 칸 기준 패턴과 바닥의 높이를 통해 구한 높이가
* 위에서 구한 높이와 같지 않으면 */
if (h != sub[j] - pattern[j]) {
isValid = false; /* 현재 떨어지는 방식이 유효하지 않음 */
break;
}
}
/* 현재 떨어지는 방식이 유효하면 방법의 수 증가 */
if (isValid) { cnt++; }
}
return cnt;
}