【BZOJ1414/3705】[ZJOI2009]对称的正方形 二分+hash

【BZOJ1414/3705】[ZJOI2009]对称的正方形

Description

Orez很喜欢搜集一些神秘的数据,并经常把它们排成一个矩阵进行研究。最近,Orez又得到了一些数据,并已经把它们排成了一个n行m列的矩阵。通过观察,Orez发现这些数据蕴涵了一个奇特的数,就是矩阵中上下对称且左右对称的正方形子矩阵的个数。 Orez自然很想知道这个数是多少,可是矩阵太大,无法去数。只能请你编个程序来计算出这个数。

Input

文件的第一行为两个整数n和m。接下来n行每行包含m个正整数,表示Orez得到的矩阵。

Output

文件中仅包含一个整数answer,表示矩阵中有answer个上下左右对称的正方形子矩阵。

Sample Input

5 5
4 2 4 4 4
3 1 4 4 3
3 5 3 3 3
3 1 5 3 3
4 2 1 2 4

Sample Output

27
数据范围
对于30%的数据 n,m≤100
对于100%的数据 n,m≤1000 ,矩阵中的数的大小≤109

题解:枚举中点,然后二分+hash即可。

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int maxn=1010;
typedef unsigned long long ull;
typedef long long ll;
int v[maxn][maxn];
int n,m;
ll ans;
ull h1[maxn][maxn],h2[maxn][maxn],h3[maxn][maxn],h4[maxn][maxn],b1[maxn],b2[maxn];
inline int rd()
{
	int ret=0,f=1;	char gc=getchar();
	while(gc<‘0‘||gc>‘9‘)	{if(gc==‘-‘)f=-f;	gc=getchar();}
	while(gc>=‘0‘&&gc<=‘9‘)	ret=ret*10+gc-‘0‘,gc=getchar();
	return ret*f;
}
int main()
{
	n=rd(),m=rd();
	int i,j,l,r,mid;
	ull g1,g2,g3,g4;
	for(i=1;i<=n;i++)	for(j=1;j<=m;j++)	v[i][j]=rd();
	for(b1[0]=b2[0]=1,i=1;i<=n;i++)	b1[i]=b1[i-1]*233,b2[i]=b2[i-1]*2333;
	for(i=1;i<=n;i++)	for(j=1;j<=m;j++)	h1[i][j]=h1[i-1][j]*233+h1[i][j-1]*2333-h1[i-1][j-1]*233*2333+v[i][j];
	for(i=1;i<=n;i++)	for(j=m;j>=1;j--)	h2[i][j]=h2[i-1][j]*233+h2[i][j+1]*2333-h2[i-1][j+1]*233*2333+v[i][j];
	for(i=n;i>=1;i--)	for(j=1;j<=m;j++)	h3[i][j]=h3[i+1][j]*233+h3[i][j-1]*2333-h3[i+1][j-1]*233*2333+v[i][j];
	for(i=n;i>=1;i--)	for(j=m;j>=1;j--)	h4[i][j]=h4[i+1][j]*233+h4[i][j+1]*2333-h4[i+1][j+1]*233*2333+v[i][j];
	for(i=1;i<=n;i++)	for(j=1;j<=m;j++)
	{
		l=1,r=min(min(i,j),min(n-i+1,m-j+1))+1;
		while(l<r)
		{
			mid=l+r>>1;
			g1=h1[i][j]-h1[i-mid][j]*b1[mid]-h1[i][j-mid]*b2[mid]+h1[i-mid][j-mid]*b1[mid]*b2[mid];
			g2=h2[i][j]-h2[i-mid][j]*b1[mid]-h2[i][j+mid]*b2[mid]+h2[i-mid][j+mid]*b1[mid]*b2[mid];
			g3=h3[i][j]-h3[i+mid][j]*b1[mid]-h3[i][j-mid]*b2[mid]+h3[i+mid][j-mid]*b1[mid]*b2[mid];
			g4=h4[i][j]-h4[i+mid][j]*b1[mid]-h4[i][j+mid]*b2[mid]+h4[i+mid][j+mid]*b1[mid]*b2[mid];
			if(g1==g2&&g1==g3&&g1==g4)	l=mid+1;
			else	r=mid;
		}
		ans+=l-1;
		l=1,r=min(min(i,j),min(n-i,m-j))+1;
		while(l<r)
		{
			mid=l+r>>1;
					g1=h1[i][j]-h1[i-mid][j]*b1[mid]-h1[i][j-mid]*b2[mid]+h1[i-mid][j-mid]*b1[mid]*b2[mid];
				j++,g2=h2[i][j]-h2[i-mid][j]*b1[mid]-h2[i][j+mid]*b2[mid]+h2[i-mid][j+mid]*b1[mid]*b2[mid];
			i++,j--,g3=h3[i][j]-h3[i+mid][j]*b1[mid]-h3[i][j-mid]*b2[mid]+h3[i+mid][j-mid]*b1[mid]*b2[mid];
				j++,g4=h4[i][j]-h4[i+mid][j]*b1[mid]-h4[i][j+mid]*b2[mid]+h4[i+mid][j+mid]*b1[mid]*b2[mid];
			i--,j--;
			if(g1==g2&&g1==g3&&g1==g4)	l=mid+1;
			else	r=mid;
		}
		ans+=l-1;
	}
	printf("%lld",ans);
	return 0;
}
时间: 2024-10-13 11:17:06

【BZOJ1414/3705】[ZJOI2009]对称的正方形 二分+hash的相关文章

bzoj 1414: [ZJOI2009]对称的正方形 manacher算法+單調隊列

1414: [ZJOI2009]对称的正方形 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 331  Solved: 149[Submit][Status] Description Orez很喜欢搜集一些神秘的数据,并经常把它们排成一个矩阵进行研究.最近,Orez又得到了一些数据,并已经把它们排成了一个n行m列的矩阵.通过观察,Orez发现这些数据蕴涵了一个奇特的数,就是矩阵中上下对称且左右对称的正方形子矩阵的个数. Orez自然很想知道这个数是

BZOJ 1414 ZJOI2009 对称的正方形 Hash+二分

题目大意:求正方形回文子矩阵数量(即左右对称.上下对称的正方形子矩阵) 正解是Manacher--但是Hash+二分是能卡过去的0.0 我太丧病了0.0 首先为了避免边长奇偶性带来的WT要把矩阵扩大二倍 然后样例就变成了这样: 00000000000 04020404040 00000000000 03010404030 00000000000 03050303030 00000000000 03010503030 00000000000 04020102040 00000000000 把这个矩

[luoguP2601] [ZJOI2009]对称的正方形(二维Hash + 二分 || Manacher)

传送门 很蒙蔽,不知道怎么搞. 网上看题解有说可以哈希+二分搞,也有的人说用Manacher搞,Manacher是什么鬼?以后再学. 对于这个题,可以从矩阵4个角hash一遍,然后枚举矩阵中的点,再二分半径. 但是得考虑边的长度为奇偶所带来的影响. 比如 1 1 1 1 这个边数为偶数的矩阵显然没法搞. 所以得在矩阵中插入0, 变成 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 具体操作就看代码好了. 然后只枚举 行 + 列 为偶数的点就行.

bzoj 1414: [ZJOI2009]对称的正方形

Description Orez很喜欢搜集一些神秘的数据,并经常把它们排成一个矩阵进行研究.最近,Orez又得到了一些数据,并已经把它们排成了一个n行m列的矩阵.通过观察,Orez发现这些数据蕴涵了一个奇特的数,就是矩阵中上下对称且左右对称的正方形子矩阵的个数. Orez自然很想知道这个数是多少,可是矩阵太大,无法去数.只能请你编个程序来计算出这个数. Input 文件的第一行为两个整数n和m.接下来n行每行包含m个正整数,表示Orez得到的矩阵. Output 文件中仅包含一个整数answer

UVA - 12338 Anti-Rhyme Pairs 二分+hash

题目链接:https://vjudge.net/problem/UVA-12338 题意: 给你n个串 问你任意两个串的最大公共前缀长度是多少 题解: 二分+hash 思路很明显,我最近用来写hash 很鸡肋 #include<bits/stdc++.h> using namespace std; #pragma comment(linker, "/STACK:102400000,102400000") #define ls i<<1 #define rs ls

【二分+hash】【字符串】【平衡树】【JSOI 2008】火星人prefix

1014: [JSOI2008]火星人prefix Time Limit: 10 Sec Memory Limit: 162 MB Submit: 4264 Solved: 1306 Description 火星人最近研究了一种操作:求一个字串两个后缀的公共前缀.比方说,有这样一个字符串:madamimadam,我们将这个字符串的各个字符予以标号:序号: 1 2 3 4 5 6 7 8 9 10 11 字符 m a d a m i m a d a m 现在,火星人定义了一个函数LCQ(x, y)

BZOJ-1014 [JSOI2008]火星人prefix (Splay+二分+hash)

题解: 用splay维护添加修改操作,然后二分hash判断长度. 操作一:对于查询区间[l,r]的hash值,显然将l-1旋到根,将r+1旋到根的右儿子,此时所求区间就是根的右儿子的左儿子了. 操作二:将要修改的位置旋到根,然后直接改就可以了. 操作三:要在x后面添加一个字符,显然将x旋到根,x+1旋到根的右儿子,然后直接加在根的右儿子的左儿子上就可以了. 1 #include<iostream> 2 #include<cstdlib> 3 #include<cstdio&g

bzoj 1014 LCP 二分 Hash 匹配

求同一字符串的两个后缀的最长公共前缀. 将字符串按位置放到Splay中维护(每个节点还维护一下该子树的hash),然后二分前缀的长度,用splay计算出指定范围的hash,按hash是否相等来判断是否相同. 一开始是将字符串看成26进制,加上unsigned long long的自然溢出来计算哈希,但这样Wa掉了,改成27进制就AC了,但我还不知道为什么,望明者相告,谢. 1 /***********************************************************

【BZOJ-1014】火星人prefix Splay + 二分 + Hash

1014: [JSOI2008]火星人prefix Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 5852  Solved: 1871[Submit][Status][Discuss] Description 火星人最近研究了一种操作:求一个字串两个后缀的公共前缀.比方说,有这样一个字符串:madamimadam,我们将这个字符串的各个字符予以标号:序号: 1 2 3 4 5 6 7 8 9 10 11 字符 m a d a m i m a d