题解 ABC025D【25個の整数】

发布时间 2023-03-22 21:18:40作者: rui_er

*3006。

数据范围明示状压 DP,但是涉及到填数不好直接状压,因为我们对每个格子只能记录一个二进制位,不可能知道每个数都在什么位置。

不妨换个思路,不难想到一个二进制位可以用来记录这个位置是否已经填数,只需要利用上这个信息。注意到我们不关心具体填的数是多少,只关心偏序关系,因此可以从小到大填数。

从小到大填数时,不存在长度为 \(3\) 的单调数列等价于:填的位置左右填数状态相同,且上下填数状态相同(填数状态指是否已经填数)。可以根据这一点判断填数是否合法,并完成转移。

时间复杂度 \(\mathcal O(2^nn)\),其中 \(n=25\)

//By: OIer rui_er
#include <bits/stdc++.h>
#define rep(x,y,z) for(int x=(y);x<=(z);x++)
#define per(x,y,z) for(int x=(y);x>=(z);x--)
#define debug(format...) fprintf(stderr, format)
#define fileIO(s) do{freopen(s".in","r",stdin);freopen(s".out","w",stdout);}while(false)
using namespace std;
typedef long long ll;

mt19937 rnd(std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::system_clock::now().time_since_epoch()).count());
int randint(int L, int R) {
	uniform_int_distribution<int> dist(L, R);
	return dist(rnd);
}

template<typename T> void chkmin(T& x, T y) {if(x > y) x = y;}
template<typename T> void chkmax(T& x, T y) {if(x < y) x = y;}

const int mod = 1e9+7;

int a[25], pos[25], dp[1<<25];

void calc(int S, int k) {
	int x = k / 5, y = k % 5;
	if(0 < x && x < 4 && ((S >> (k - 5)) & 1) != ((S >> (k + 5)) & 1)) return;
	if(0 < y && y < 4 && ((S >> (k - 1)) & 1) != ((S >> (k + 1)) & 1)) return;
	dp[S] = (dp[S] + dp[S^(1<<k)]) % mod;
}

int main() {
	memset(pos, -1, sizeof(pos));
	rep(i, 0, 24) {
		scanf("%d", &a[i]);
		if(a[i]) pos[a[i]-1] = i;
	}
	dp[0] = 1;
	rep(S, 1, (1<<25)-1) {
		int u = __builtin_popcount(S) - 1;
		if(pos[u] >= 0) calc(S, pos[u]);
		else rep(i, 0, 24) if((S >> i) & 1) calc(S, i);
	}
	printf("%d\n", dp[(1<<25)-1]);
	return 0;
}