Freezing
Freezing
首先有一个朴素的 \(DP\) , dp[i][j]
表示前 \(i\) 个人中,且最后一个人的状态是 \(j\) 的方案数,然后暴力转移即可,复杂度 \(O(n \times 2^m)\) 无法通过本题。
有一个不能通过本题的优化是,我们可以通过枚举 ~j
的子集,来进行状态转移,赛时可以通过,赛后被出题人卡掉了。代码如下
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
#define rep(i, a, b) for (int i(a); i <= b; ++ i )
#define dec(i, a, b) for (int i(b); i >= a; -- i )
template <typename T> void chkmax(T &x, T y) { x = max(x, y); }
template <typename T> void chkmin(T &x, T y) { x = min(x, y); }
const int MOD = 998244353;
inline int mod(int x) {return x >= MOD ? x - MOD : x;}
inline int ksm(int a, int b) {
int ret = 1; a = mod(a);
for(; b; b >>= 1, a = 1LL * a * a % MOD) if(b & 1) ret = 1LL * ret * a % MOD;
return ret;
}
template<int MOD>
struct modint {
int x;
modint() {x = 0; }
modint(int y) {x = y;}
inline modint inv() const { return modint{ksm(x, MOD - 2)}; }
explicit inline operator int() { return x; }
friend inline modint operator + (const modint &a, const modint& b) { return modint(mod(a.x + b.x)); }
friend inline modint operator - (const modint &a, const modint& b) { return modint(mod(a.x - b.x + MOD)); }
friend inline modint operator * (const modint &a, const modint& b) { return modint(1ll * a.x * b.x % MOD); }
friend inline modint operator / (const modint &a, const modint& b) { return modint(1ll * a.x * b.inv().x % MOD); }
friend inline modint operator + (const modint &a, const int& b) { return modint(mod(a.x + b)); }
friend inline modint operator - (const modint &a, const int& b) { return modint(mod(a.x - b + MOD)); }
friend inline modint operator * (const modint &a, const int& b) { return modint(1ll * a.x * b % MOD); }
friend inline modint operator / (const modint &a, const int& b) { return modint(1ll * a.x * ksm(b, MOD - 2) % MOD); }
friend inline modint operator - (const modint &a) { return modint(mod(MOD - a.x)); }
friend inline modint& operator += (modint &a, const modint& b) { return a = a + b; }
friend inline modint& operator -= (modint &a, const modint& b) { return a = a - b; }
friend inline modint& operator *= (modint &a, const modint& b) { return a = a * b; }
friend inline modint& operator /= (modint &a, const modint& b) { return a = a / b; }
friend inline modint& operator += (modint &a, const int& b) { return a = a + b; }
friend inline modint& operator -= (modint &a, const int& b) { return a = a - b; }
friend inline modint& operator *= (modint &a, const int& b) { return a = a * b; }
friend inline modint& operator /= (modint &a, const int& b) { return a = a / b; }
friend auto &operator >> (istream &i, modint &a) {return i >> a.x; }
friend auto &operator << (ostream &o, const modint &z) { return o << z.x; }
inline bool operator == (const modint &b) { return x == b.x; }
inline bool operator != (const modint &b) { return x != b.x; }
inline bool operator < (const modint &a) { return x < a.x; }
inline bool operator <= (const modint &a) { return x <= a.x; }
inline bool operator > (const modint &a) { return x > a.x; }
inline bool operator >= (const modint &a) { return x >= a.x; }
operator int() const {
return x;
}
// inline void
};
typedef modint<MOD> mint;
inline mint ksm(mint a, int b, mint ret = 1) {
for(; b; b >>= 1, a = a * a ) if(b & 1) ret = ret * a ;
return ret;
}
constexpr int N = 2E5 + 10;
int n, m;
mint f[1 << 16];// g[1 << 16];
// f[i][j] 表示高八位为 i 且 低八位和j没有交集的方案数
void solve() {
cin >> n >> m;
int all = (1 << m) - 1;
for (int i = 1; i <= n; i ++ ) {
string s; cin >> s;
int x = 0;
for (int j = 0; j < m; j ++ ) if (s[j] - 'h') {
x |= 1 << j;
}
int y = x ^ all;
mint now = f[0] + 1;
for (int j = y; j; j = (j - 1) & y) {
now += f[j];
}
f[x] += now;
}
mint ans = 0;
for (int i = 0; i < (1 << m); i ++ ) ans += f[i];
cout << ans << "\n";
}
int main() {
cin.tie(nullptr)->sync_with_stdio(false);
solve();
return 0;
}
下面考虑正确的 \(DP\) ,可以把 \(m\) 位二进制数拆分为两个 \(\frac{m}{2}\) 位的数字。令 dp[i][j]
表示高八位为 \(i\) 且 低八位和 \(j\) 没有交集的方案数,然后进行转移即可,具体过程可以参考代码。
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
#define rep(i, a, b) for (int i(a); i <= b; ++ i )
#define dec(i, a, b) for (int i(b); i >= a; -- i )
template <typename T> void chkmax(T &x, T y) { x = max(x, y); }
template <typename T> void chkmin(T &x, T y) { x = min(x, y); }
const int MOD = 998244353;
inline int mod(int x) {return x >= MOD ? x - MOD : x;}
inline int ksm(int a, int b) {
int ret = 1; a = mod(a);
for(; b; b >>= 1, a = 1LL * a * a % MOD) if(b & 1) ret = 1LL * ret * a % MOD;
return ret;
}
template<int MOD>
struct modint {
int x;
modint() {x = 0; }
modint(int y) {x = y;}
inline modint inv() const { return modint{ksm(x, MOD - 2)}; }
explicit inline operator int() { return x; }
friend inline modint operator + (const modint &a, const modint& b) { return modint(mod(a.x + b.x)); }
friend inline modint operator - (const modint &a, const modint& b) { return modint(mod(a.x - b.x + MOD)); }
friend inline modint operator * (const modint &a, const modint& b) { return modint(1ll * a.x * b.x % MOD); }
friend inline modint operator / (const modint &a, const modint& b) { return modint(1ll * a.x * b.inv().x % MOD); }
friend inline modint operator + (const modint &a, const int& b) { return modint(mod(a.x + b)); }
friend inline modint operator - (const modint &a, const int& b) { return modint(mod(a.x - b + MOD)); }
friend inline modint operator * (const modint &a, const int& b) { return modint(1ll * a.x * b % MOD); }
friend inline modint operator / (const modint &a, const int& b) { return modint(1ll * a.x * ksm(b, MOD - 2) % MOD); }
friend inline modint operator - (const modint &a) { return modint(mod(MOD - a.x)); }
friend inline modint& operator += (modint &a, const modint& b) { return a = a + b; }
friend inline modint& operator -= (modint &a, const modint& b) { return a = a - b; }
friend inline modint& operator *= (modint &a, const modint& b) { return a = a * b; }
friend inline modint& operator /= (modint &a, const modint& b) { return a = a / b; }
friend inline modint& operator += (modint &a, const int& b) { return a = a + b; }
friend inline modint& operator -= (modint &a, const int& b) { return a = a - b; }
friend inline modint& operator *= (modint &a, const int& b) { return a = a * b; }
friend inline modint& operator /= (modint &a, const int& b) { return a = a / b; }
friend auto &operator >> (istream &i, modint &a) {return i >> a.x; }
friend auto &operator << (ostream &o, const modint &z) { return o << z.x; }
inline bool operator == (const modint &b) { return x == b.x; }
inline bool operator != (const modint &b) { return x != b.x; }
inline bool operator < (const modint &a) { return x < a.x; }
inline bool operator <= (const modint &a) { return x <= a.x; }
inline bool operator > (const modint &a) { return x > a.x; }
inline bool operator >= (const modint &a) { return x >= a.x; }
operator int() const {
return x;
}
// inline void
};
typedef modint<MOD> mint;
inline mint ksm(mint a, int b, mint ret = 1) {
for(; b; b >>= 1, a = a * a ) if(b & 1) ret = ret * a ;
return ret;
}
constexpr int N = 2E5 + 10;
int n, m, a[N];
mint f[1 << 8][1 << 8];
// f[i][j] 表示高八位为 i 且 低八位和j没有交集的方案数
void solve() {
cin >> n >> m;
mint ans = 0;
for (int i = 1; i <= n; i ++ ) {
string s; cin >> s;
for (int j = 0; j < m; j ++ ) if (s[j] - 'h') {
a[i] |= 1 << j;
}
int x = a[i] >> 8, y = a[i] & ((1 << 8) - 1);
mint z = 1;
for (int i = 0; i < 1 << 8; i ++ ) if (!(x & i)) {
z += f[i][y];
}
ans += z;
for (int i = 0; i < 1 << 8; i ++ ) if (!(y & i)) {
f[x][i] += z;
}
}
cout << ans << "\n";
}
int main() {
cin.tie(nullptr)->sync_with_stdio(false);
solve();
return 0;
}