背景

(创作声明:包含虚构创作)

从阴暗潮湿的沼泽地里莫名其妙生长出红树的那一刻起,有一种传说中的红皮肤怪物诞生了。
它们在沼泽莲叶上发出诱人的声响,它们身形小巧,穿梭于水草和藤蔓之间。
第一天便有经过的傻子村民被它生吞。村民们找遍了沼泽外围,连尸体都没看见。和他一齐失踪的还有常年在附近山地生活,偶尔下沼泽地喝水的一只山羊。
第二天,村民们隔着老远听到女巫的惨叫。当提着剑和牛奶的村民们赶到附近时,只见到红皮肤魔头远遁的身影。
村民们一向惧怕的女巫,也未能幸免。
沼泽附近的村庄笼罩在恐惧的阴霾中
……
几个月后,一位身着蓝色铠甲的勇者来到村庄,他有很多东西,与村民们进行了友好的交易,借住在村民的小木屋里。不大的村庄里充满了快活的空气。后一天村民们发现,一直以来守护村庄的强大铁人不见了。
那一天,村民们回想起了被红皮肤魔头支配的恐惧
勇者决定亲自去沼泽地探查,我和另外几个胆大的村民跟了上去。
结果却极其惨烈……上帝啊!我们为勇者默哀!
当我们到达沼泽地外围时,就迎面遇上了魔头!勇者毫无戒备地走了过去,说:“这不就是个……”
话音未落,他就被魔头一口吞掉了!
我们逃得很慌,根本来不及回头看,只听见魔头近似嬉笑的响声渐渐远去……

——沼泽村庄图书馆:《村民回忆录》

题面

当Mojump更新了1.19快照版本时,std通过摸鱼了解了所有更新的内容,包括沼泽红树林,红色的青蛙,泥土等等……

他当然也知道了青蛙刚出来时有个特性,那就是吞噬一切,包括玩家。所以他打算到沼泽地去探险。

他开启了一个新地图,并出生在了一块 n×mn\times mn×m 的沼泽地。该地有些位置的草地刚触及海平面,另一些位置则低了一格,被水填充。

std 现在有一个有意思的问题,由于种子特性,地图生成时该区域 n×mn\times mn×m 个位置各有一个独立的概率 ai,ja_{i,j}ai,j​ 会低一格,被水填充。他想知道最大的单个水池面积的期望大小。单个水池可以理解为一个被水填充的格子的极大四联通块

ai,ja_{i,j}ai,j​ 和答案都对 998244353998244353998244353 取模。(并不是用多项式的暗示)

1≤n,m≤40,n×m≤40.1\leq n,m\leq 40,n\times m\leq 40.1≤n,m≤40,n×m≤40.

题解

(由于题解极其简略,我多花了点时间在题面背景上摸鱼)

由于 n×mn\times mn×m 不超过 40,所以 n,mn,mn,m 的最小值不超过 6,我们不妨设 n≤mn\leq mn≤m ,那么我们可以维护当前列的连通性、每个连通块的大小、扫过的连通块的最大大小——作为状态(!)设计DP。

当你用精心打磨出的大模拟爆搜对总合法状态数进行计算后,会发现它最大在 1.6e51.6e51.6e5 左右。

实现上,把连通性用最小表示法后,大可直接用 map 存 DP 状态(pair<int,string>,最大大小和连通块大小都不超过 40,可以用 char)然后带一个 log⁡\loglog 的全局复杂度加成进行计算。不然,可能有亿点麻烦。

最终用插头DP的方式转移,总时间复杂度 O(可过)O(可过)O(可过) (两个log⁡\loglog?)。

CODE

pair 的第一位存的是最小表示法,第二位的 string 第一个位置存最大连通块大小,后面的位置存当前列每个连通块的大小。

#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<stack>
#include<random>
#include<bitset>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 500005
#define LL long long
#define ULL unsigned long long
#define ENDL putchar('\n')
#define DB double
#define lowbit(x) (-(x) & (x))
#define FI first
#define SE second
int xchar() {static const int maxn = 1000000;static char b[maxn];static int pos = 0,len = 0;if(pos == len) pos = 0,len = fread(b,1,maxn,stdin);if(pos == len) return -1;return b[pos ++];
}
//#define getchar() xchar()
LL read() {LL f = 1,x = 0;int s = getchar();while(s < '0' || s > '9') {if(s<0)return -1;if(s=='-')f=-f;s = getchar();}while(s >= '0' && s <= '9') {x = (x<<1) + (x<<3) + (s^48);s = getchar();}return f*x;
}
void putpos(LL x) {if(!x)return ;putpos(x/10);putchar((x%10)^48);}
void putnum(LL x) {if(!x) {putchar('0');return ;}if(x<0) putchar('-'),x = -x;return putpos(x);
}
void AIput(LL x,int c) {putnum(x);putchar(c);}const int MOD = 998244353;
int n,m,s,o,k;
int a[45][45],pr[45][45];
int id[5000005],to[5000005],tp[5000005],cnt;
int findd(int S) {int ty[10] = {},xx = S,a[10],cn = 0,c2 = 0,rs = 0;while(xx) a[++ cn] = xx%10,xx /= 10;for(int i = cn;i > 0;i --) {if(a[i] && !ty[a[i]]) ty[a[i]] = ++ c2;rs = rs*10+ty[a[i]];}return id[rs];
}
string ep = "0";
pair<int,string> dw(int S,string b) {int ty[10] = {},xx = S,a[10],cn = 0,c2 = 0,rs = 0,mx = b[0];int bu[10] = {};string re = ep; re[0] = (char)mx;for(int i = 1;i < (int)b.size();i ++) bu[i] = b[i];while(xx) a[++ cn] = xx%10,xx /= 10;for(int i = cn;i > 0;i --) {if(a[i] && !ty[a[i]]) ty[a[i]] = ++ c2,re += (char)bu[a[i]];rs = rs*10+ty[a[i]];}return {rs,re};
}
void dfs(int x,int mx,int S) {if(x > n) {id[S] = ++ cnt;to[cnt] = S; tp[cnt] = mx;return ;}for(int i = 0;i <= mx+1;i ++) {dfs(x+1,max(mx,i),S*10+i);}return ;
}
map<pair<int,string>,int> dp,pd;
int pw[10];
int main() {freopen("memory.in","r",stdin);freopen("memory.out","w",stdout);n = read(); m = read();int mx = 0;for(int i = 1;i <= n;i ++) {for(int j = 1;j <= m;j ++) {pr[i][j] = read();mx = max(mx,pr[i][j]);}}if(n > m) {swap(n,m);for(int i = 1;i <= n;i ++) {for(int j = 1;j <= m;j ++) {a[i][j] = pr[j][i];}}}else {for(int i = 1;i <= n;i ++) {for(int j = 1;j <= m;j ++) {a[i][j] = pr[i][j];}}}dfs(0,0,0);pw[0] = 1;for(int i = 1;i <= n;i ++) pw[i] = pw[i-1]*10;for(int i = 1;i < pw[n];i ++) {id[i] = findd(i);}ep[0] = (char)0;dp[{0,ep}] = 1;for(int c = 1;c <= m;c ++) {for(int r = 1;r <= n;r ++) {pd.clear(); swap(pd,dp);for(auto i = pd.begin();i != pd.end();i ++) {int mx = i->FI.SE[0],s = i->FI.FI;string bu = (i->FI).SE;int l = (s/pw[r-1])%10,u = (r==1 ? 0:(s/pw[r-2])%10);pair<int,string> nx = dw(s-l*pw[r-1],bu);(dp[nx] += i->SE*1ll*(MOD+1-a[r][c])%MOD) %= MOD;if(a[r][c]) {if(!l && !u) {int ma = tp[id[s]]; bu[0] = (char)max(mx,1);bu += (char)1;nx = dw(s+(ma+1)*pw[r-1],bu);(dp[nx] += i->SE*1ll*a[r][c]%MOD) %= MOD;}else if(!l || !u || l == u) {int ii = max(l,u);bu[ii] = bu[ii]+1; bu[0] = (char)max(mx,(int)bu[ii]);nx = dw(s-l*pw[r-1]+ii*pw[r-1],bu);(dp[nx] += i->SE*1ll*a[r][c]%MOD) %= MOD;}else {for(int j = 0;j < n;j ++) {if((s/pw[j])%10 == u) {s += (l-u)*pw[j];}}bu[l] += bu[u]+1; bu[0] = (char)max(mx,(int)bu[l]);nx = dw(s,bu);(dp[nx] += i->SE*1ll*a[r][c]%MOD) %= MOD;}}}}}int ans = 0;for(auto i = dp.begin();i != dp.end();i ++) {(ans += ((int)i->FI.SE[0])*1ll*i->SE%MOD) %= MOD;}AIput(ans,'\n');return 0;
}

后记

该特性的首次发现,便是一只青蛙吞了一只山羊。

“剑和牛奶”:众所周知,牛奶可以针对性地对女巫造成极大伤害(

勇者:“这不就是个癞蛤蟆?”

铁傀儡为什么消失了?这个不清楚,我只知道勇者的背包里多了四块铁锭。

【模拟赛】沼泽地(插头DP)相关推荐

  1. [2020.11.25NOIP模拟赛]下棋【dp】

    正题 题目链接:https://www.luogu.com.cn/problem/U142297?contestId=37766 题目大意 nnn个白棋mmm个黑棋排成一排,要求没有任何一段黑白棋的个 ...

  2. [2020.11.25NOIP模拟赛]出租车【dp】

    正题 题面链接:https://www.luogu.com.cn/problem/U142298?contestId=37766 题目大意 nnn个人有起点和终点,按顺序上车,但下车可以任意顺序,车最 ...

  3. 送分题 - hzw模拟赛Test10

    送分题 - hzw模拟赛Test10 树形Dp 题解: 啊啊啊好难啊,送分题都这么难... • 这题可以先设 f(u; k) 表示以节点 u 为根的⼦树中,在⽅案 合法的情况下,选中的点中离 u 最近 ...

  4. 20190405 DP模拟赛1总结

    20190405 DP模拟赛1总结 概况 重要的模型&&方法:T1,T2,T3 分数 失分原因 题目及其题解 [ T1:Lg P2737 [USACO4.1]麦香牛块Beef McNu ...

  5. 2020年蓝桥杯模拟赛2020.3.25直播笔记

    2020年蓝桥杯模拟赛解题报告(CPP版本) 第八题 长草的bfs写法[我想暴力模拟O kmn] 深搜会爆 bfs像投到水里的涟漪 问题: const int dx[] = {1, 0, -1, 0} ...

  6. 2021年 第12届 蓝桥杯 第3次模拟赛真题详解及小结【Java版】

    蓝桥杯 Java B组 省赛决赛 真题详解及小结汇总[2013年(第4届)~2021年(第12届)] 第11届 蓝桥杯-第1.2次模拟(软件类)真题-(2020年3月.4月)-官方讲解视频 说明:大部 ...

  7. 2020年 第11届 蓝桥杯 第2次模拟赛真题详解及小结【Java版】

    蓝桥杯 Java B组 省赛真题详解及小结汇总[2013年(第4届)~2020年(第11届)] 注意:部分代码及程序 源自 蓝桥杯 官网视频(历年真题解析) 郑未老师. 2013年 第04届 蓝桥杯 ...

  8. 10.30 NFLS-NOIP模拟赛 解题报告

    总结:今天去了NOIP模拟赛,其实是几道USACO的经典的题目,第一题和最后一题都有思路,第二题是我一开始写了个spfa,写了一半中途发现应该是矩阵乘法,然后没做完,然后就没有然后了!第二题的暴力都没 ...

  9. 蓝桥杯省内模拟赛解题过程

    今天参加了一场蓝桥杯模拟赛,做的咋样还不知道,本博客的题解只是我提交的代码,并不代表真正的题解,大家参考着看看吧. 题目一 问题描述 将LANQIAO中的字母重新排列,可以得到不同的单词,如LANQI ...

最新文章

  1. linux编程之GDB调试
  2. 怎样将outlook express中的邮件保留在原邮箱
  3. 开源软件通识基础:第二周课程回顾与总结
  4. 变压器符号_行输出变压器的结构、符号及电路分析
  5. SQLite剖析之异步IO模式、共享缓存模式和解锁通知
  6. 上海抽查35批次手机 3批次比吸收率标识要求不合格
  7. 自写保存字符串或文件为asp.net缓存的类
  8. mysql工具分页原理_高效的mysql分页方法及原理
  9. 苹果iOS捷径(快捷指令)自动填写表单
  10. 斐讯 http文件服务器,斐讯刷机教程(硬件版本A5)
  11. widows安装wxPython
  12. Airbnb如何应用AARRR策略成为全球第一民宿平台
  13. XlsReadWriteII 版本声明操作要点
  14. 千锋Unity学习笔记
  15. Linux--Configure Proxy
  16. Python实现Chrome浏览器书签自动备份
  17. 2016如果你想改变命运,请花几分钟读一下:(网上创业赚钱项目)
  18. Watir vs. SilkTest
  19. Mysql-基础命令
  20. iOS9 iOS10 iOS11的新特性

热门文章

  1. au3打开html文件,autoit获取ie浏览器简单操作网页(GUI小工具)
  2. 网银显示服务器无效应,网银U盾报错“系统智能卡服务未启动”的解决方案
  3. 甲骨文与亚马逊的云业务之争
  4. 微信开发者工具 网络连接失败
  5. 优酷海外用户自动显示简体页面方法
  6. Python之使用LRU缓存策略进行缓存
  7. Catch That Cow(详解)
  8. 2018年阅读书籍清单
  9. Spring 5 + Spring MVC 5 + MyBatis 3 的 Maven 项目集成
  10. 前端:运用js和jquery实现风琴图