[20220307联考] 回忆

前言

插头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
连通信息(最小表示法)、各连通块大小、历史最大、 
*/

posted @ 2022-03-07 20:25  皮皮刘  阅读(47)  评论(0编辑  收藏  举报