前言
插头dp弱弱化版,或许对入门一点帮助都没有。
题目
\(\operatorname{512MiB,3s}\)
给定整数 \(n,m\) ,以及 \(n\times m\) 各个格子翻正的概率,一个状态的权值为极大四连通块大小,问权值期望。
哦对了,要对 \(998244353\) 取模。
\(1\le n,m\le 40;1\le n\times m\le 40.\)
讲解
让我看看有多少人只看到了数据范围前半截而没看到后半截,嘿嘿。
考试的时候有个数据范围的表,更容易看飞。
我看到又怎样,我又不会插头dp。
好了好了,来讲一个能过的做法。
假设 \(n\le m\),我们可以对每一列维护一些信息,如果知道概率的话,其实就已经做完了。
思考我们要维护啥,无非就是连通性(要用最小表示法),各个连通块大小,历史连通块大小的最大值。
于是我们暴力的搞两个 vector
,用一个 pair
rua在一起,然后丢到 map
里面暴力转移。
不知道复杂度,总之能过。
代码
学习了讲题人的代码
//12252024832524
#pragma GCC optimize("Ofast")
#include <bits/stdc++.h>
#define TT template<typename T>
using namespace std;
typedef long long LL;
const int MAXN = 45;
const int MOD = 998244353;
int n,m;
int a[MAXN][MAXN];
LL Read()
{
LL x = 0,f = 1;char c = getchar();
while(c > '9' || c < '0'){if(c == '-')f = -1;c = getchar();}
while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
return x * f;
}
TT void Put1(T x)
{
if(x > 9) Put1(x/10);
putchar(x%10^48);
}
TT void Put(T x,char c = -1)
{
if(x < 0) putchar('-'),x = -x;
Put1(x); if(c >= 0) putchar(c);
}
TT T Max(T x,T y){return x > y ? x : y;}
TT T Min(T x,T y){return x < y ? x : y;}
TT T Abs(T x){return x < 0 ? -x : x;}
void Add(int &x,int y){x += y;if(x >= MOD) x -= MOD;if(x < 0) x += MOD;}
typedef pair<vector<int>,vector<int>> ors;//outrageous
map<ors,int> dp[2];
int f[10],tag[10];
int findSet(int x){
if(f[x] ^ x) f[x] = findSet(f[x]);
return f[x];
}
ors operator + (const ors &A,const int S){
ors ret; ret.first.resize(n);
for(int i = 0;i <= n;++ i) f[i] = i,tag[i] = 0;//attention! <= not < (tag)
for(int i = 0;i < n;++ i)
if(S >> i & 1){
if(S >> (i+1) & 1) f[findSet(i+1)] = findSet(i);
if(A.first[i]){
for(int j = i+1;j < n;++ j)
if(S >> j & 1 && A.first[i] == A.first[j]) f[findSet(i)] = findSet(j);
}
}
int cnt = 0;//最小表示法
for(int i = 0;i < n;++ i) if((S >> i & 1) && f[i] == i) ret.first[i] = ++cnt;
for(int i = 0;i < n;++ i) if(S >> i & 1) ret.first[i] = ret.first[findSet(i)];
ret.second.resize(cnt+1);
for(int i = 0;i < n;++ i)
if(ret.first[i]){
if(A.first[i] && !tag[A.first[i]]){
tag[A.first[i]] = 1;
ret.second[ret.first[i]] += A.second[A.first[i]];
}
++ret.second[ret.first[i]];
}
ret.second[0] = A.second[0];
for(int i = 1;i <= cnt;++ i) ret.second[0] = Max(ret.second[0],ret.second[i]);
return ret;
}
int main()
{
freopen("memory.in","r",stdin);
freopen("memory.out","w",stdout);
n = Read(); m = Read();
for(int i = 0;i < n;++ i)
for(int j = 0;j < m;++ j)
n < m ? a[i][j] = Read() : a[j][i] = Read();
if(n > m) swap(n,m);
bool now = 0; dp[0][{vector<int>(n),{0}}] = 1;
for(int i = 0;i < m;++ i){
bool to = now^1;
for(int S = 0;S < (1<<n);++ S){
int mul = 1;
for(int j = 0;j < n;++ j)
if(S >> j & 1) mul = 1ll * mul * a[j][i] % MOD;
else mul = mul * (MOD+1ll-a[j][i]) % MOD;
if(!mul) continue;
for(auto &A : dp[now]) Add(dp[to][A.first+S],1ll*A.second*mul%MOD);
}
dp[now].clear(); now = to;
}
int ans = 0;
for(auto &A : dp[now]) ans = (ans + 1ll * A.first.second[0] * A.second) % MOD;
Put(ans,'\n');
return 0;
}
/*
1 5
1 2 3 4 5
ans:121
连通信息(最小表示法)、各连通块大小、历史最大、
*/