zoukankan      html  css  js  c++  java
  • 【BZOJ1414】对称的正方形(ZJOI2009)-Manacher+RMQ

    测试地址:对称的正方形
    做法: 本题需要用到Manacher+RMQ。
    首先,我们想到枚举正方形的对称轴,求对称轴交点为某个点时的最大正方形大小。为了方便,我们模仿Manacher算法,把矩阵用00扩充成一个(2n+1)×(2m+1)(2n+1) imes (2m+1)的矩阵,这样我们就只用考虑中心点是整数坐标的情况了。
    我们思考怎么判断一个中心点向外延伸kk的长度的正方形是否合法。首先正方形要左右对称,那么中间的对称轴上的2k+12k+1个点,它们在各自行中以它们为中心的最长回文子串长度,应该大于等于2k+12k+1,显然这个长度可以用Manacher算法算出。上下对称也是同理。可是暴力判断是O(n3)O(n^3)的,会爆炸,我们需要找到另一种思路。
    我们分开考虑一个中心点的上、下、左、右四个边界,问题就转化为,求右端点给定时,使得区间内数都大于等于某个数的最左的左端点是哪一个。注意到右端点右移时,左端点也是单调右移的,这时我们配合RMQ就可以解决这个问题了。求出某中心点的四个边界后,求最小值就是这个中心点的边界了,这之后讨论答案就很简单了。
    于是总的时间复杂度为O(n2logn)O(n^2log n),可以通过此题。
    以下是本人代码:

    #include <bits/stdc++.h>
    using namespace std;
    int n,m,a[2010][2010]={0},r[2010][2010],c[2010][2010];
    int s[2010],len[2010],mn[2010][15],p[2010];
    int lft[2010][2010],rht[2010][2010],up[2010][2010],down[2010][2010];
    
    void Manacher(int n)
    {
    	int center=1,rht=1;
    	len[1]=1;
    	for(int i=2;i<=n;i++)
    	{
    		if (i>rht||i+len[2*center-i]-1>=rht)
    		{
    			len[i]=(i>rht)?0:(rht-i+1);
    			while(i-len[i]>=1&&i+len[i]<=n&&s[i-len[i]]==s[i+len[i]])
    				len[i]++;
    			center=i;
    			rht=i+len[i]-1;
    		}
    		else len[i]=len[2*center-i];
    	}
    }
    
    void init_RMQ(int n)
    {
    	p[0]=0;
    	for(int i=1;i<=n;i++)
    	{
    		if ((1<<(p[i-1]+1))<i)
    			p[i]=p[i-1]+1;
    		else p[i]=p[i-1];
    		mn[i][0]=s[i];
    	}
    	
    	for(int i=1;i<=12;i++)
    		for(int j=1;j<=n-(1<<i)+1;j++)
    			mn[j][i]=min(mn[j][i-1],mn[j+(1<<(i-1))][i-1]);
    }
    
    int query(int l,int r)
    {
    	int x=r-l+1;
    	return min(mn[l][p[x]],mn[r-(1<<p[x])+1][p[x]]);
    }
    
    int main()
    {
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=m;j++)
    			scanf("%d",&a[2*i-1][2*j-1]);
    	n=2*n-1,m=2*m-1;
    	
    	for(int i=1;i<=n;i++)
    	{
    		for(int j=1;j<=m;j++)
    			s[j]=a[i][j];
    		Manacher(m);
    		for(int j=1;j<=m;j++)
    			r[i][j]=len[j];
    	}
    	for(int i=1;i<=m;i++)
    	{
    		for(int j=1;j<=n;j++)
    			s[j]=a[j][i];
    		Manacher(n);
    		for(int j=1;j<=n;j++)
    			c[j][i]=len[j];
    	}
    	
    	for(int i=1;i<=n;i++)
    	{
    		for(int j=1;j<=m;j++)
    			s[j]=c[i][j];
    		init_RMQ(m);
    		int x=1;
    		for(int j=1;j<=m;j++)
    		{
    			while(query(x,j)<j-x+1) x++;
    			lft[i][j]=j-x+1;
    		}
    		x=m;
    		for(int j=m;j>=1;j--)
    		{
    			while(query(j,x)<x-j+1) x--;
    			rht[i][j]=x-j+1;
    		}
    	}
    	for(int i=1;i<=m;i++)
    	{
    		for(int j=1;j<=n;j++)
    			s[j]=r[j][i];
    		init_RMQ(n);
    		int x=1;
    		for(int j=1;j<=n;j++)
    		{
    			while(query(x,j)<j-x+1) x++;
    			up[j][i]=j-x+1;
    		}
    		x=n;
    		for(int j=n;j>=1;j--)
    		{
    			while(query(j,x)<x-j+1) x--;
    			down[j][i]=x-j+1;
    		}
    	}
    	
    	int ans=0;
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=m;j++)
    			if (i%2==j%2)
    			{
    				int x=min(min(up[i][j],down[i][j]),min(lft[i][j],rht[i][j]));
    				ans+=(x+(i%2))>>1;
    			}
    	printf("%d",ans);
    	
    	return 0;
    }
    
  • 相关阅读:
    系统开发——页面跳转函数的书写
    PC 端自动化最佳方案
    access 点滴
    调用outlook发邮件
    mac安装vmware fusion后设置vmnet8上网
    Gin框架国内安装教程
    mac下一些vscode的初始化设置和使用
    Mac: 使用SDK切换gradle版本
    transfer.sh -- 使用 curl 从命令行上传文件并返回下载地址的文件分享服务(可自架服务端)
    使用ffmpeg转码,解决IDM从youtube下下来的视频在机顶盒上放不出声音问题(使用GPU加速)
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793242.html
Copyright © 2011-2022 走看看