Luogu P9646 SNCPC2019 Paper-cutting 题解 [ 紫 ] [ manacher ] [ 贪心 ] [ 哈希 ] [ BFS ]
Paper-cutting:思维很好,但代码很构式的 manacher 题。
蒟蒻 2025 年切的第一道题,是个紫,并且基本独立想出的,特此纪念。
判断能否折叠
我们先考虑一部分能折叠需要满足什么条件。显然,这一部分需要是一个长度为偶数的回文串。
那么横向和纵向会不会影响呢?答案是不会,因为横向折了之后,折过去的部分一定是对称的,那么只要原来某两列相等,这之后这两列还是相等的。我们可以画图理解:

相同颜色的部分代表这两部分相等。
于是,我们只需要对每行每列哈希一遍,然后利用偶数回文串折叠即可。同时我们也得出横行和纵列互不影响的结论。
这一部分可以用 manacher 快速求出最大折叠半径。
折叠策略
我们先考虑这个问题的弱化版。
只能折一边时
在只能折一边时,我们一定会尽可能地折叠。这样一定更优。
证明也是容易的,折叠前折叠部分单独的连通块会被砍掉,与剩余部分相连接的连通块因为是对称过去,所以折叠过去后一定存在一个格子使它们依然连通。
因此,折叠后连通块的个数一定不会增加,答案也一定不劣。同时,折叠的顺序也是无影响的,因为每次能折叠当且仅当偶数回文串内存在一个能折叠到的点。
两边都能折时
同样是能折就折,因为左折、右折互不影响,我们可以用分类讨论来证明。
右折后左折能正常进行时
显然左右都能操作。
右折后左折被挡住一部分时
这种情况一定不存在。
左折要被挡住,必然要满足下图:

而橙色的左折显然不合法,这种情况等效于浅橙色部分右折。
因此能折就折的策略一定不劣。
翻折实现
对每行每列跑 manacher 记录最长回文长度后,我们考虑设计 dp。
以向左翻折为例,定义 \(dp_i\) 表示以 \(i\) 为最后一列是否可行,\(d_i\) 为回文半径长度,则:
\]
这个式子可以用前缀和优化、单调队列优化来做,或者跟我一样维护一个最近的 \(1\) 的指针也可以。我本来用前缀和优化做的,但是挂了几十发并且还调不出就用其他题解一样的方式维护指针了。
注意初始化 \(dp_n=1\)。
向左翻折后,从左边开始再往右翻折一遍就可以了。注意不能翻过新的右边界。
最后求出左右边界、上下边界后,进行 BFS 求出需要减掉的连通块个数即可。
时间复杂度 \(O(nm)\),注意动态开数组。
代码
非常构式,调了 2h。
#include <bits/stdc++.h>
#define fi first
#define se second
#define lc (p<<1)
#define rc ((p<<1)|1)
#define eb(x) emplace_back(x)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
using pi=pair<int,int>;
const ull nosol1=1145141919810,nosol2=1919810114514,nosol3=1911451419810;
int n,m,x,ansxl,ansxr,ansyl,ansyr,d[2000005];
int gox[]={0,0,1,-1};
int goy[]={-1,1,0,0};
vector<int>c[1000005];
ull xhs[1000005],yhs[1000005],f[2000005];
void init(int len,ull *arr)
{
x=0;
f[0]=nosol1;
f[++x]=nosol2;
for(int i=1;i<=len;i++)f[++x]=arr[i],f[++x]=nosol2;
f[x+1]=nosol3;
}
void manacher()
{
for(int i=1;i<=x;i++)d[i]=0;
d[1]=1;
for(int i=2,l=0,r=0;i<=x;i++)
{
if(i<=r)d[i]=min(r-i+1,d[l+r-i]);
while(f[i-d[i]]==f[i+d[i]])d[i]++;
if(i+d[i]-1>r)l=i-d[i]+1,r=i+d[i]-1;
}
}
void do_dp(int len,int &ansl,int &ansr)
{
ansr=len;
for(int i=len-1;i>=1;i--)
{
int p=i*2+1;
int dx=d[p]/2;
if(i+dx>=ansr)ansr=i;
}
ansl=1;
for(int i=1;i<ansr;i++)
{
int p=i*2+1;
int dx=d[p]/2;
if(i-dx+1<=ansl)ansl=i+1;
}
}
bool legal(int x,int y){return (x>=ansxl&&x<=ansxr&&y>=ansyl&&y<=ansyr);}
void bfs(int x,int y)
{
queue<pi>q;
q.push({x,y});
c[x][y]=1;
while(!q.empty())
{
pi u=q.front();
q.pop();
int nx=u.fi,ny=u.se;
for(int i=0;i<4;i++)
{
int tx=nx+gox[i],ty=ny+goy[i];
if(legal(tx,ty)&&c[tx][ty]==0)
{
q.push({tx,ty});
c[tx][ty]=1;
}
}
}
}
void solve()
{
int ans=0;
cin>>n>>m;
for(int i=1;i<=n;i++)c[i].clear();
for(int i=1;i<=n;i++)xhs[i]=0;
for(int i=1;i<=m;i++)yhs[i]=0;
for(int i=1;i<=n;i++)
{
c[i].eb(0);
for(int j=1;j<=m;j++)
{
char tmp;
cin>>tmp;
int k=tmp-'0';
xhs[i]=xhs[i]*2+k;
yhs[j]=yhs[j]*2+k;
c[i].eb(k);
}
}
init(n,xhs);
manacher();
do_dp(n,ansxl,ansxr);
init(m,yhs);
manacher();
do_dp(m,ansyl,ansyr);
for(int i=ansxl;i<=ansxr;i++)
{
for(int j=ansyl;j<=ansyr;j++)
{
if(c[i][j]==0)
{
ans++;
bfs(i,j);
}
}
}
cout<<ans<<'\n';
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
cin>>t;
while(t--)solve();
return 0;
}
Luogu P9646 SNCPC2019 Paper-cutting 题解 [ 紫 ] [ manacher ] [ 贪心 ] [ 哈希 ] [ BFS ]的更多相关文章
- luogu P1126 机器人搬重物 题解
luogu P1126 机器人搬重物 题解 题目描述 机器人移动学会(\(RMI\))现在正尝试用机器人搬运物品.机器人的形状是一个直径\(1.6\)米的球.在试验阶段,机器人被用于在一个储藏室中搬运 ...
- TOJ 2541: Paper Cutting
2541: Paper Cutting Time Limit(Common/Java):1000MS/10000MS Memory Limit:65536KByteTotal Submit: ...
- 【BZOJ4755】扭动的回文串(Manacher,哈希)
[BZOJ4755]扭动的回文串(Manacher,哈希) 题面 BZOJ 题解 不要真的以为看见了回文串就是\(PAM,Manacher\)一类就可以过. 这题显然不行啊. 我们主要考虑如何解决跨串 ...
- Luogu 1351 NOIP 2014 联合权值(贪心,计数原理)
Luogu 1351 NOIP 2014 联合权值(贪心,计数原理) Description 无向连通图 G 有 n 个点,n-1 条边.点从 1 到 n 依次编号,编号为 i 的点的权值为 Wi, ...
- Luogu 1084 NOIP2012 疫情控制 (二分,贪心,倍增)
Luogu 1084 NOIP2012 疫情控制 (二分,贪心,倍增) Description H 国有 n 个城市,这 n 个城市用 n-1 条双向道路相互连通构成一棵树, 1 号城市是首都, 也是 ...
- Luogu 1080 【NOIP2012】国王游戏 (贪心,高精度)
Luogu 1080 [NOIP2012]国王游戏 (贪心,高精度) Description 恰逢H国国庆,国王邀请n位大臣来玩一个有奖游戏.首先,他让每个大臣在左.右手上面分别写下一个整数,国王自己 ...
- Luogu 1315 【NOIP2011】观光公交 (贪心)
Luogu 1315 [NOIP2011]观光公交 (贪心) Description 风景迷人的小城Y 市,拥有n 个美丽的景点.由于慕名而来的游客越来越多,Y 市特意安排了一辆观光公交车,为游客提供 ...
- Luogu 1525 【NOIP2010】关押罪犯 (贪心,并查集)
Luogu 1525 [NOIP2010]关押罪犯 (贪心,并查集) Description S城现有两座监狱,一共关押着N名罪犯,编号分别为1~N.他们之间的关系自然也极不和谐.很多罪犯之间甚至积怨 ...
- 洛谷P2507 [SCOI2008]配对 题解(dp+贪心)
洛谷P2507 [SCOI2008]配对 题解(dp+贪心) 标签:题解 阅读体验:https://zybuluo.com/Junlier/note/1299251 链接题目地址:洛谷P2507 [S ...
- 洛谷P1484 种树&洛谷P3620 [APIO/CTSC 2007]数据备份 题解(堆+贪心)
洛谷P1484 种树&洛谷P3620 [APIO/CTSC 2007]数据备份 题解(堆+贪心) 标签:题解 阅读体验:https://zybuluo.com/Junlier/note/132 ...
随机推荐
- 元数建模工具之chiner
chiner,发音:[kaɪˈnər],使用React+Electron+Java技术体系构建的一款元数建模平台. 下载地址:https://gitee.com/robergroup/chiner/r ...
- Pycharm之使用git merge合并分支
当我们在某个分支上代码开发完成,代码测试没问题后需要把分支上的代码合并到 master 分支上.这样保证 master 分支的代码永远都是最新的,也是最干净的,这样才可以持续的开发自己的项目.本篇讲解 ...
- IDEA 一个服务同时启动多个实例
有些时候为了测试,服务的分发,我们可以在IDEA中对一个微服务启动多个实例,配置方法如下: 1.编辑同时启动的微服务. 勾选 allow parallel run 2.编辑完成后复制一份配置. 3.复 ...
- API开发与管理规范v1.0
1. 协议规范 为了确保不同业务系统之间以及前后端的的数据交互的快捷性,通讯协议统一约定如下: 对内调用的API接口统一使用 HTTP协议 对外互联网发布的API建议使用HTTPS协议也可以使用HTT ...
- R数据分析:临床研究样本量计算、结果解读与实操
很久之前给大家写过一篇文章详细介绍了样本量计算的底层逻辑,不过那篇文章原理是依照卡方比较来写的,可以拓展到均值比较,但视角还是比较小,今天从整个临床研究的角度结合具体的例子谈谈大家遇到的样本量的计算方 ...
- 中电金信多模态鉴伪技术抵御AI造假威胁
AI换脸技术,属于深度伪造最常见方式之一,是一种利用人工智能生成逼真的虚假人脸图片或视频的技术.基于深度学习算法,可以将一个人的面部特征映射到另一个人的面部,创造出看似真实的伪造内容.近年来,以A ...
- Python中所有子图标签Legend显示详解
在数据可视化中,图例(legend)是一个非常重要的元素,它能够帮助读者理解图表中不同元素的含义.特别是在使用Python进行可视化时,matplotlib库是一个非常强大的工具,能够轻松创建包含多个 ...
- 程序员出海做 AI 工具:如何用 similarweb 找到最佳流量渠道?
如题,今天给大家带来实操的一个小教程.这里先抛出个问题:"做海外流量增长,如何为产品制定营销渠道?" 分享一个方法只需要 3 步,方法如下: 找到和你产品最接近的细分 Top 竞争 ...
- 【XML】学习笔记第二章-dtd
目录 XML-DTD DTD语句 基本声明语句 引用外部DTD DTD元素 四种元素类型 元素定义关键字 修饰符号 DTD中的属性 属性修饰 属性类型 DTD中的实体和符号 符号 坑 XML-DTD ...
- 【转载】茅台巽风app地图详解,做任务不迷路,纯手绘
茅台发布了新的app"巽风" 根据"巽值"的排名,发放20000个虎年茅台的资格,还是可以玩一玩的 哪些途径获取"巽值" 1.做任务,和游戏 ...