• [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
    连通信息(最小表示法)、各连通块大小、历史最大、 
    */
    
    
  • 相关阅读:
    DockerAPI版本不匹配的问题
    Linux文件系统
    队列

    多维数组
    字符串
    线性表
    ARM编辑、编译工具
    南京IT公司
    数据结构:用单链表实现的队列(2)
  • 原文地址:https://www.cnblogs.com/PPLPPL/p/15977817.html
Copyright © 2020-2023  润新知