首页 > 其他分享 >Luogu P9646 SNCPC2019 Paper-cutting 题解 [ 紫 ] [ manacher ] [ 贪心 ] [ 哈希 ] [ BFS ]

Luogu P9646 SNCPC2019 Paper-cutting 题解 [ 紫 ] [ manacher ] [ 贪心 ] [ 哈希 ] [ BFS ]

时间:2025-01-01 17:07:30浏览次数:1  
标签:manacher int 题解 折叠 左折 Luogu dp 回文

Paper-cutting:思维很好,但代码很构式的 manacher 题。

蒟蒻 2025 年切的第一道题,是个紫,并且基本独立想出的,特此纪念。

判断能否折叠

我们先考虑一部分能折叠需要满足什么条件。显然,这一部分需要是一个长度为偶数的回文串。

那么横向和纵向会不会影响呢?答案是不会,因为横向折了之后,折过去的部分一定是对称的,那么只要原来某两列相等,这之后这两列还是相等的。我们可以画图理解:

相同颜色的部分代表这两部分相等。

于是,我们只需要对每行每列哈希一遍,然后利用偶数回文串折叠即可。同时我们也得出横行和纵列互不影响的结论。

这一部分可以用 manacher 快速求出最大折叠半径。

折叠策略

我们先考虑这个问题的弱化版。

只能折一边时

在只能折一边时,我们一定会尽可能地折叠。这样一定更优。

证明也是容易的,折叠前折叠部分单独的连通块会被砍掉,与剩余部分相连接的连通块因为是对称过去,所以折叠过去后一定存在一个格子使它们依然连通。

因此,折叠后连通块的个数一定不会增加,答案也一定不劣。同时,折叠的顺序也是无影响的,因为每次能折叠当且仅当偶数回文串内存在一个能折叠到的点。

两边都能折时

同样是能折就折,因为左折、右折互不影响,我们可以用分类讨论来证明。

右折后左折能正常进行时

显然左右都能操作。

右折后左折被挡住一部分时

这种情况一定不存在。

左折要被挡住,必然要满足下图:

而橙色的左折显然不合法,这种情况等效于浅橙色部分右折。

因此能折就折的策略一定不劣

翻折实现

对每行每列跑 manacher 记录最长回文长度后,我们考虑设计 dp。

以向左翻折为例,定义 \(dp_i\) 表示以 \(i\) 为最后一列是否可行,\(d_i\) 为回文半径长度,则:

\[dp_i=[(\sum_{j=i+1}^{i+d_i}dp_j) >0] \]

这个式子可以用前缀和优化、单调队列优化来做,或者跟我一样维护一个最近的 \(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;
}

标签:manacher,int,题解,折叠,左折,Luogu,dp,回文
From: https://www.cnblogs.com/zhr0102/p/18646092

相关文章

  • CF601E A Museum Robbery 题解
    题目传送门前置知识线段树与离线询问解法普通的回退背包无法处理本题中的删除操作,考虑线段树分治后转化为只进行添加的背包。具体实现时可以对每个深度开一个背包的转移数组,时间复杂度为\(O(nk\logq+qk)\),可以接受。代码#include<bits/stdc++.h>usingnamespacestd;#......
  • 洛谷 P11487 「Cfz Round 5」Gnirts 10——题解
    洛谷P11487「CfzRound5」Gnirts10传送锚点摸鱼环节「CfzRound5」Gnirts10题目背景Englishstatement.YoumustsubmityourcodeattheChineseversionofthestatement.InMemoryof\(\text{F}\rule{66.8px}{6.8px}\).题目描述题面还是简单一点好。给定......
  • 题解:AT_abc386_d [ABC386D] Diagonal Separation
    分析题面,发现题目求的是是否存在一个白点被\((1,1)\)和任意一个黑点围成的矩形内。先将所有黑点按\(x\)坐标排序。枚举所有的白点。找到所有横坐标不比该白点横坐标小的所有黑点的纵坐标的最大值所属点。如果该点的纵坐标小于该白点的纵坐标:(蓝点代表题目中的白点,红点......
  • 百丽宫24年春季真题题解——回型字符的打印
    不妨参考之前的一道选做题思路(虽然楼主自己之前也没找到时间做)但当时瞟过一眼题目,个人认为可以一起做,都是你中有我的关系,思路很相似。选做题摘录——晕:看着这样的"回”形图案你晕吗?让我们不用数组,来做出它。输入格式:n。正方形的边长输出格式:"%3d"   边长为n的数字回......
  • 百丽宫22年真题题解——最短路径(排列组合法)
    #include<stdio.h>unsignedlonglonghigh;unsignedlonglonglow;unsignedlonglongfac(intn,intm){unsignedlonglongi,f=1;if(m!=1){for(i=n;i>=n-m+1;i--){f=f*i;}returnf;}elseif(m......
  • 自动推理与规划:让机器具备智能决策与问题解决能力
    随着人工智能技术的不断进步,自动推理与规划(AutomatedReasoningandPlanning)已经成为使机器具备高效决策和问题解决能力的核心技术之一。它涉及如何通过逻辑推理、任务规划和约束求解,使机器能够自主地解决复杂问题、制定行动策略,并在不断变化的环境中做出最优决策。自动推理......
  • 题解:CF727F Polycarp's problems
    link。贪心做法。本题贪心做法的实质就是用整数尽量多地抵消该整数后面的负数。如果正着做,没有办法考虑全该数后面的所有负数,所以倒着做。例如当前遍历到了\(50\),此时序列如下:\[\dots,50,-50,-10,-20\]易得我们\(50\)应该抵消的是\(-10,-20\),而不是前面的\(-50\),因为......
  • Web 前端面试指南与高频考题解析
    Web前端面试指南与高频考题解析准备:简历编写和面试前准备一般来说,跳槽找工作要经历投递简历、准备面试、面试和谈offer四个阶段。其中面试题目会因你的等级和职位而异,从入门级到专家级,广度和深度都会有所增加。不过,不管什么级别和职位,面试题目一般都可以分类为理论知识、算法......
  • 2024 idea安装教程以及常见的问题解答(附激活至2026 实际永久 亲测)
    2024idea安装激活使用教程以及常见的问题解答,例如会反复跳出激活码并提示无效,显示Keyisinvalid或者打不开等下载IDEA安装包也可以在这里点击下载idea(含博主使用版本)下载idea安装IDEA点击xx关掉程序!下载补丁IntelliJIDEA补丁文件点击获取补丁下载成功后,打开......
  • IDEA 2024.3安装及激活教程(附详细步骤和常见问题解答,激活至2026实际永久,亲测)
    前言IntelliJIDEA是JetBrains公司推出的一款强大IDE,最新版IDEA2024.3.1.1在功能和体验上进一步提升。本文为大家提供最详细的安装、激活教程,帮助您快速配置开发环境。文末附激活补丁获取方式及常见问题解决方案。1.卸载旧版本IDEA如果电脑中已安装旧版本IDEA,请先彻......