3.4模拟赛T4 题解
步骤一:枚举
注意到本题的 \(n,m\le 50\) 使得我们不能直接对 o 进行状压。因为 o 可能有很多个。
而题目又给了数据的另一个特点就是* 不超过 \(12\) 个。
所以可能会存在 \(O(2^{*的个数})\) 这样的一个时间复杂度。
所以考虑将所有的 o 取为中心,而 * 我们用二进制来枚举每一个*是否取为中心。
那么问题就转化为对于格子中,已经规定了一些点取为中心,求在此情况下摆放积木的方案数。
步骤二:判断无解、根据限制条件建图
我们发现,对于一个中心,如果可以,我们有 \(4\) 种放置方式。而当我们放置一个积木后,可能会导致周围的中心格子积木的放置方式变少。因为不能重叠。
我们称 使一个中心的放置方式确定且唯一 为 定向。
我们现在要放黑色圆圈这个位置,那么可能导致放置方式受影响的格子为下图:

那么受影响的格子分为三类:红格子(相邻),黄格子(有一个角公共),蓝格子(中间隔了一个位置)。
情况一: 红格子(相邻)
注意此时因为这两个格子(一红一黑)相邻,所以这两个格子的放置方式是定的,也就是被定向了。
也就是当黑格子周围有且仅有一个红格子或 超出边界的格子时,黑格子被定向了。
但是如果黑格子周围有 \(>1\) 个红格子或 超出边界的格子,会导致黑格子无论如何都放不了。此情况是无解的。
粗略来讲,这个红格子只是指代一定填不了的格子。
情况二:蓝格子(中间隔了一个格子)
此时中间隔的格子一定是空的。(不然会被上一步返回无解)
如果其中有且仅有一个被定向了,那么另一个也会被跟着定向。可以理解为 定向的传导。因为中间的格子是空的,所以被定向的格子一定是因为黄色格子被占而导致该格子放置时一定占了二者中间的位置。从而导致另一个格子也被定向。

此后任意时候,如果其中的任意一个被定向了,那么另一个也会被定向。所以考虑将两个点之间连一条边,那么当一个点被定向后,它所在的连通块都会被定向。所以考虑用并查集维护这个边。
但是如果此前二者都被定向了,那么都会占中间的格子。所以无解。
特殊情况:构成环
我们在按上述情况进行建边时可能会遇到环,如下图:

在连通块内没有任意一点被定向时,一共有两种情况。但是这两种情况所占的格子是一样的。是不是可以认为为某种意义上的“被定向了”呢?
此时我们可以模拟建边的过程。加入最后一条边时,两个点属于同一个连通块,如果两个点都被定向了,则无解,因为会重叠。否则就可以视作为某种意义上的“被定向了”,不过方案数需要 \(\times 2\)。
为了区分,我们在并查集中维护值 \(t=1\) 表示被非严格定向了(也就是包含这种情况),以及 \(t2=1\) 表示严格定向(不包含这种情况)。
那么对于合并点 \(x,y\)
如果 \(fa_x=fa_y\)
当 \(t_{fa_x}=1\) 时无解。否则让 \(t_{fa_x}=1\),不更新 \(t2_{fa_x}\),因为 \(t2\) 不管这种情况。
如果 \(fa_x\neq fa_y\)
让 \(x\gets fa_x,y\gets fa_y\)。
当 \(t_{x}=t_{y}=1\) 时无解。否则让 \(fa_x=y\),\(t_y|=t_x\),\(t2_y|=t2_x\)。
情况三:黄格子(有一个角公共)
此时如果没有被定向,则一共存在两种情况。

而他们占的格子也一样。
其实这个东西与上面那个特殊情况很一样啊。因为有两种情况,且占的格子都一样。
而且还有更一样的,就是在一个连通块内,最多只会出现一个这个相邻的,或者那个成环的,否则无解。
因为这两个东西相当于定向了,当一个连通块被不同方向同时定了向时是无解的。
所以这个东西可以类似上面的维护。
直接进行连边。而且因为这种情况需要更改 \(t\) ,不更改 \(t2\)。所以需要再进行一次连边。
另一种理解方式
可以用 < 和 > 理解。
对于一次传导,相当于存在 >>>>或<<<< 的箭头。而上面的特殊情况和相邻情况相当于 <>。所以最终会形成一个类似 <<<<<<<>>>>>>。这也可以说明一个连通块最多只存在一个 <>。
步骤三:计算方案数
总方案为每个连通块的方案数的乘积。
分类讨论:
被严格定向,即 \(t2=1\)
此时方案数为 \(1\)。
没有被定向(包括非严格),即 \(t=0,t2=0\)
为了方便计算,我们还需在并查集过程中记录变量 \(sz\),表示连通块大小。
则无论怎么填我们都会空一个位置。(自己画图理解)
而且我们发现,我们想让哪个位置空都行(除了中心)。
所以方案数为中心的四联通格子。
每个中心有 \(4\) 个四联通格子。但是会有重复。因为不存在环,所以此时并查集结构为一棵树。重复格子有 \(sz-1\) 个。
所以方案数为 \(4\times sz-(sz-1)=3\times sz+1\)。
被非严格定向,没有被严格定向,即 \(t=1,t2=0\)
此时方案数为 \(2\)。
最后最终答案就是枚举过程中各个答案之和。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define LL long long
inline int read(){
char c=getchar();bool f=0;int x=0;
while(c > '9' || c < '0') f|=c=='-',c=getchar();
while(c >= '0'&&c <= '9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
if(f) x=-x;return x;
}
const int N = 60,MOD = 1e9 + 7;
const int dx[5] = {0,0,-1,1},dy[5] = {-1,1,0,0};
int n,m,t[N * N],t2[N * N],fa[N * N],id[N][N],sz[N * N];
LL ans;
int mp[N][N],pow_2[N];
bool flag;
std::vector<std::pair<int,int> > k;
bool check(int x,int y)
{
if(x < 1 || y < 1 || x > n || y > m) return true;
return false;
}
int find(int x)
{
return x == fa[x] ? x : fa[x] = find(fa[x]);
}
void merge(int x,int y)
{
y = find(y);
x = find(x);
if(x == y)
{
if(t[x]) return flag = true, void();
t[x] = 1;
}else
{
if(t[x] && t[y]) return flag = true, void();
t[y] |= t[x];
t2[y] |= t2[x];
fa[x] = y;
sz[y] += sz[x];
}
}
void solve()
{
int idx = 0;
flag = false;
for(int i = 1;i <= n;++i)
{
for(int j = 1;j <= m;++j)
{
if(mp[i][j])
{
sz[++idx] = 1;
fa[idx] = idx;
id[i][j] = idx;
t[idx] = t2[idx] = 0;
}else id[i][j] = 0;
}
}
for(int i = 1;i <= n;++i)
{
for(int j = 1;j <= m;++j)
{
if(!mp[i][j]) continue;
int cnt = 0;
for(int dir = 0;dir < 4;++dir)
{
int tx = i + dx[dir];
int ty = j + dy[dir];
cnt += int(check(tx,ty) || mp[tx][ty]);
}
if(cnt > 1) return ;
if(cnt == 1) t[id[i][j]] = t2[id[i][j]] = 1;
//在接下来的合并当中,我们不会把四个方向都考虑,因为一定可以到另一个点的时候,再考虑这个点的另外两个方向,省时间复杂度。
if(!check(i - 2,j) && mp[i - 2][j]) merge(id[i - 2][j],id[i][j]);
if(!check(i,j - 2) && mp[i][j - 2]) merge(id[i][j - 2],id[i][j]);
if(!check(i - 1,j + 1) && mp[i - 1][j + 1])
{
merge(id[i - 1][j + 1],id[i][j]);
merge(id[i - 1][j + 1],id[i][j]);//合并两次,第二次实际上是为了判断是否有解
}
if(!check(i - 1,j - 1) && mp[i - 1][j - 1])
{
merge(id[i - 1][j - 1],id[i][j]);
merge(id[i - 1][j - 1],id[i][j]);
}
if(flag) return ;
}
}
LL res = 1;
for(int i = 1;i <= idx;++i)
{
if(find(i) == i && !t2[i])
{
if(!t[i]) res = res * 1ll * (3 * sz[i] + 1) % MOD;
else res = res * 2 % MOD;
}
}
ans += res;
if(ans >= MOD) ans -= MOD;
}
int main()
{
pow_2[0] = 1;
for(int i = 1;i <= 15;++i) pow_2[i] = pow_2[i - 1] << 1;
n = read();
m = read();
for(int i = 1;i <= n;++i)
{
char ch[N];
scanf("%s",ch + 1);
for(int j = 1;j <= m;++j)
{
if(ch[j] == '*') k.push_back({i,j});
else if(ch[j] == 'o') mp[i][j] = 1;
}
}
int num = k.size();
for(int i = 0;i < pow_2[num];++i)
{
for(int j = 0;j < num;++j) if(i & pow_2[j]) mp[k[j].first][k[j].second] = 1;
solve();
for(int j = 0;j < num;++j) if(i & pow_2[j]) mp[k[j].first][k[j].second] = 0;
}
printf("%lld",ans);
return 0;
}
3.4模拟赛T4 题解的更多相关文章
- 【洛谷比赛】[LnOI2019]长脖子鹿省选模拟赛 T1 题解
今天是[LnOI2019]长脖子鹿省选模拟赛的时间,小编表示考的不怎么样,改了半天也只会改第一题,那也先呈上题解吧. T1:P5248 [LnOI2019SP]快速多项式变换(FPT) 一看这题就很手 ...
- 2020.3.23 模拟赛游记 & 题解
这次的模拟赛,实在是水. 数据水,\(\texttt{std}\) 水,出题人水,做题人也水.??? 游记就说一句: 水. T1 metro 弱智题. 人均 \(100pts\). #pragma G ...
- 20180606模拟赛T4——数学游戏
数学游戏 题目描述: 小T又发脑残了,没错,她又要求奇怪的东西,这次她想知道[X,Y]之间整数有多少可以表示成K个不同的B的幂的和形势.如\(x,y,k,b=15,20,2,2\),则有: \[17= ...
- 2017-9-10"切题如切菜杯"模拟赛T4 ZZI
题目 YYH拿到了父亲给的钱欣喜若狂,把这些钱拿来造了n栋房子.现在他要给这些房子通电.他有两种方法:第一种是在房间里搭核电发电机发电,对于不同的房子,他需要花不同的代价Vi:,第二种是将有电的房子i ...
- 洛谷[LnOI2019]长脖子鹿省选模拟赛 简要题解
传送门 听说比赛的时候T4T4T4标程锅了??? WTF换我时间我要写T3啊 于是在T4T4T4调半天无果的情况下260pts260pts260pts收场真的是tcltcltcl. T1 快速多项式变 ...
- 20180610模拟赛T4——木棍
有\(N\)根木棍,每根的长度\(L\)和重量\(W\)已知.这些木棍将被一台机器一根一根地加工.机器需要一些启动时间来做准备工作,启动时间与木棍被加工的具体情况有关.启动时间遵循以下规则: 加工第一 ...
- 2023 年 CCF 春季测试赛模拟赛 - 2 题解
T1 约数和 标准解法 \(n = a_1^{b_1} \times a_2^{b_2} \dots a_k^{b_k}\) 那么根据算术基本定理的推广,约数个数和约数和都是可以快速计算得到 约数和 ...
- NOIP模拟赛10 题解
t3: 题意 给你一棵树,然后每次两种操作:1.给一个节点染色 : 2. 查询一个节点与任意已染色节点 lca 的权值的最大值 分析 考虑一个节点被染色后的影响:令它的所有祖先节点(包括自身)的所有除 ...
- BJOI 模拟赛 #3 题解
T1 一个网格,每个点有权值,求有多少条路径权值乘积不小于 $n$ $R,C \leq 300, n \leq 10^6$ sol: 暴力 dp 是 $O(R \times C \times n)$ ...
- BJOI 2019 模拟赛 #2 题解
T1 完美塔防 有一些空地,一些障碍,一些炮台,一些反射镜 障碍会挡住炮台的炮, 反射镜可以 90° 反射炮台的光线,炮台可以选择打他所在的水平一条线或者竖直一条线 求是否有一组方案满足每个空地必须要 ...
随机推荐
- 玩转代码:深入GitHub,高效管理我们的“shou学”平台源代码
玩转代码:深入GitHub,高效管理我们的"shou学"平台源代码 在当今快节奏的开发世界中,有效地管理代码不仅仅是一种良好实践,更是一种必需.无论您是独立开发者还是大型团队的一员 ...
- Linux 在文件中统计关键字出现的次数
摘要:在当前文件或者当前目录下所有文件中,使用Linux命令grep.awk.sed.rg或者cat统计关键字出现的次数. 目录 问题背景 解决办法 使用grep和wc命令 使用awk命令模式匹配 使 ...
- Ai数学基础
数学基础 1.梯度 1.1偏导数 1.1.1定义 1.1.2几何意义 1.2方向导数 1.2.1定义 1.2.2定理 注:主要运用上面那个公式来计算! 1.3梯度的概念 注:gradf 表示梯度! 1 ...
- Go中的文件操作
文件操作 读取文件的内容并显示在终端(带缓冲区的方式),使用os.Open,file.Close,bufio.NewReader(),reader.ReadString函数和方法. package m ...
- opencv检测物体颜色
#include<opencv2/opencv.hpp> #include<opencv2/opencv_modules.hpp> #include<iostream&g ...
- 快来玩玩便捷、高效的Demo练习场
Demo练习场 Vonajs 提供了一个 Demo 练习场的功能,允许我们非常方便.快捷的对代码做测试,对想法做验证 步骤 简而言之,Demo练习场的使用只需两步:第一步写代码,第二步执行终端命令.具 ...
- 中信建投X袋鼠云:实时数仓,证券机构的“速度与稳定”
近年来,<政府工作报告>多次提及"数字经济",今年更是以独立篇章,详细解读和明确了如何"促进数字经济发展".数字经济的全产业推进,为金融业带来更多元 ...
- [深度学习] 超长文,一篇讲完 NVIDIA Jetson Hello AI World 全部教程(推理 & 训练)
目录 一.开始 1.1 安装 1.2 sdkmanger 细节介绍 1.2.1 host 和 target 配合 1.2.2 Jetson SDK Components 介绍 二.Hello AI W ...
- Java 常用线程池
快捷创建线程池方式 Java通过Executors提供四种线程池,分别为: 1.newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需求,可以灵活回收空闲线程,若无可回收 ...
- C# Winform 自定义 时间线 控件
推荐 官网 http://www.hzhcontrols.com/ NetWinform自定义控件 English README.md(github) English README.md(gitee) ...