[ABC025D] 25個の整数
考虑从小到大加入每种权值,同时维护一个状压数组 $f$。
具体的,我们对于在输入矩形中出现过的数,直接转移;否则枚举当前的数放在哪一位,判断合法性之后转移就行了。
判断合法其实有一种比较简便的方法,每次加入一个数时,如果他在某个三元组的中间位置,且左右的点恰好填了一个,那么就证明这个三元组是单调的,当前方案不合法。每个三元组都会在加入中间元素时被判断一次。
这样做的复杂度保证在于:矩阵中至少已经填了 $5$ 个数。分析一下不难发现它可以做到 $O(20\times 2^{20})$。我的代码中采用了一种时间略高,但实现简便的方法。
#include <bits/stdc++.h>
#define FL(i, a, b) for(int i = (a); i <= (b); ++i)
#define FR(i, a, b) for(int i = (a); i >= (b); --i)
using namespace std;
const int p = 1e9 + 7;
int x, id[27], f[1 << 25];
bool check(int s, int i){
if(i % 5 && i % 5 < 4 && (s >> i - 1 & 1 ^ s >> i + 1 & 1)) return 0;
return !(i / 5 && i / 5 < 4 && (s >> i - 5 & 1 ^ s >> i + 5 & 1));
}
int main(){
fill(id, id + 26, -1), f[0] = 1;
FL(i, 0, 24) scanf("%d", &x), x? (id[x] = i) : 0;
FL(s, 1, (1 << 25) - 1){
int b = __builtin_popcount(s);
if(~id[b]){if(check(s, id[b]))
(f[s] += f[s ^ (1 << id[b])]) %= p;
}
else FL(i, 0, 24) if(check(s, i))
(f[s] += f[s ^ (1 << i)]) %= p;
}
printf("%d\n", f[(1 << 25) - 1]);
return 0;
}

浙公网安备 33010602011771号