您的位置:首页 > 其它

ZJOI 2009 对称的正方形 RMQ+Manacher

2017-01-22 12:06 316 查看
题目链接点我点我:-)

题目描述

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

输入输出格式

输入格式

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

输出格式

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

思路

1. 把矩阵变成(2n-1)*(2m-1)的,即在数字中间补上0

2.(Manacher算法)定义lx[][], ly[][]数组分别表示对于每个矩阵中的数,它的左右(上下)的最长延伸回文串的长度

3.定义left[][]数组表示对于每个矩阵中的数,以它为中心,最多可以向左边延伸的半个矩形(保证上下回文)的长度(长度应小于所有在范围内的ly[][]的最小值)

4. 那么对于left[i]数组,它的左边界(j-left[i])是递增不减的,所以可以结合RMQ在O(n2)的时间内求出来

5. 所以right[][], up[][], down[][]数组定义类似

6. 那么最终的答案为min(left[][], right[][], up[][], down[][])的和再减去包含0的部分!

感想

很遗憾本题并没有自己想出来,本来是拿它当基础练手的,洛谷的难度评定是普及+提高-,还是觉得有点难度的。这种题目如果自己可以想出来一定是个大飞跃。

另:以后写代码力争简洁,把代码写得像有水平的人写的!

代码

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>

using namespace std;

#define LL long long
#define pb push_back
#define Set(a, v) memset(a, v, sizeof(a))
#define For(i, a, b) for(int i = (a); i <= (int)(b); i++)
#define Forr(i, a, b) for(int i = (a); i >= (int)(b); i--)

#define LOG (10+5)
#define MAXN (2000+5)

int f[MAXN][MAXN];
int map[MAXN][MAXN], lx[MAXN][MAXN], ly[MAXN][MAXN];

void read(int &x){
char ch = getchar();
while(ch < '0' || ch > '9') ch = getchar();
x = 0;
while(ch >= '0' && ch <= '9'){
x = x*10+ch-'0';
ch = getchar();
}
}

int s[MAXN], Min[MAXN][LOG], Log[MAXN];

void Manacher(int *len, int n){
int p0 = 0;
For(i, 1, n){
int pos = p0+len[p0];
if(pos > i && len[2*p0-i]<(pos-i)) len[i] = len[2*p0-i];
else{
if(pos > i) len[i] = pos-i;
while(i+len[i] < n && i-len[i] > 1 && s[i+len[i]+1]==s[i-len[i]-1]) ++len[i];
p0 = i;
}
}
}

void makeRMQ(int x[MAXN][MAXN], int h, int n){
Set(Min, 0x3f);
For(i, 1, n) Min[i][0] = x[i][h];
For(j, 1, 11) For(i, 1, n){
if((i+(1<<j)-1) > n) break;
Min[i][j] = min(Min[i][j-1], Min[i+(1<<(j-1))][j-1]);
}
}

int query(int L, int R){
int k = Log[R-L+1];
return min(Min[L][k], Min[R-(1<<k)+1][k]);
}

int main(){
int n, m;
read(n); read(m);

For(i, 2, n) Log[i] = Log[i>>1]+1;
For(i, 1, n) For(j, 1, m) read(map[i*2-1][j*2-1]);
n = n*2-1, m = m*2-1;

For(i, 1, n){
For(j, 1, m) s[j] = map[i][j];
Manacher(lx[i], m);
}
For(i, 1, m){
For(j, 1, n) s[j] = map[j][i];
Manacher(ly[i], n);
}

Set(f, 0x3f);
For(i, 1, n){
makeRMQ(ly, i, m);
int v = 1;
For(j, 1, m){
while(v<j && query(v, j) < (j-v)) v++;
f[i][j] = min(f[i][j], j-v);
}
v = m;
Forr(j, m, 1){
while(v>j && query(j, v) < (v-j)) v--;
f[i][j] = min(f[i][j], v-j);
}
}

For(i, 1, m){
makeRMQ(lx, i, n);
int v = 1;
For(j, 1, n){
while(v<j && query(v, j) < (j-v)) v++;
f[j][i] = min(f[j][i], j-v);
}
v = n;
Forr(j, n, 1){
while(v>j && query(j, v) < (v-j)) v--;
f[j][i] = min(f[j][i], v-j);
}
}

int ans = 0;
For(i, 1, n) For(j, 1, m){
if((i&1)&&(j&1)) ans += (f[i][j]>>1)+1;
else if(!(i&1) && !(j&1)) ans += (f[i][j]+1)>>1;
}
printf("%d\n", ans);

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: