【BZOJ1414】對稱的正方形(ZJOI2009)-Manacher+RMQ

測試地址:對稱的正方形
作法: 本題須要用到Manacher+RMQ。
首先,咱們想到枚舉正方形的對稱軸,求對稱軸交點爲某個點時的最大正方形大小。爲了方便,咱們模仿Manacher算法,把矩陣用 0 0 擴充成一個 ( 2 n + 1 ) × ( 2 m + 1 ) (2n+1)\times (2m+1) 的矩陣,這樣咱們就只用考慮中心點是整數座標的狀況了。
咱們思考怎麼判斷一箇中心點向外延伸 k k 的長度的正方形是否合法。首先正方形要左右對稱,那麼中間的對稱軸上的 2 k + 1 2k+1 個點,它們在各自行中以它們爲中心的最長迴文子串長度,應該大於等於 2 k + 1 2k+1 ,顯然這個長度能夠用Manacher算法算出。上下對稱也是同理。但是暴力判斷是 O ( n 3 ) O(n^3) 的,會爆炸,咱們須要找到另外一種思路。
咱們分開考慮一箇中心點的上、下、左、右四個邊界,問題就轉化爲,求右端點給定時,使得區間內數都大於等於某個數的最左的左端點是哪個。注意到右端點右移時,左端點也是單調右移的,這時咱們配合RMQ就能夠解決這個問題了。求出某中心點的四個邊界後,求最小值就是這個中心點的邊界了,這以後討論答案就很簡單了。
因而總的時間複雜度爲 O ( n 2 log n ) O(n^2\log n) ,能夠經過此題。
如下是本人代碼:php

#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;
}
相關文章
相關標籤/搜索