2021年中国大学生程序设计竞赛女生专场 gym103389F 地图压缩

  • Post author:
  • Post category:其他



F. 地图压缩

你正在参与一款2D游戏的地图绘制,你拥有的美术素材是一张 n×n 的像素图片,从上到下依次编号为第 1 行到第 n 行,从左往右依次编号为第 1 列到第 n 列,其中第 i 行第 j 列的像素坐标为 (i,j),它的内容可以用一个小写字母 pi,j 来表示。

你希望从这张素材中裁剪出一个连续的矩形区域作为游戏的地图。你进行了 q 次尝试,每次尝试中你将会选择以 (x1,y1) 为左上角、(x2,y2) 为右下角的矩形部分(包括端点)作为游戏的地图。通过不断地尝试,你发现可以通过应用”四方连续”的方法来压缩地图。”四方连续纹样”是指一个单位矩形纹样向上下左右四个方向反复连续循环排列所产生的纹样,例如样例中以 (1,1) 和 (3,7) 为对角线端点的矩形可以看作以 (1,1) 和 (2,3) 为对角线端点的矩形向上下左右四个方向反复连续循环排列所产生的,该矩形包含 2×3=6 个像素点。注意,纹样在生成地图的过程中可以超出边缘,超出边缘的部分将被忽略。

请写一个程序,对于每次尝试的地图,找到包含像素点数量最少的单位矩形纹样,使得它利用四方连续可以生成该次尝试的地图。

Input

第一行包含两个正整数 n 和 q (1≤n≤2000, 1≤q≤10000),分别表示素材的尺寸和尝试的次数。

接下来 n 行,第 i 行包含一个长度为 n 的小写字符串,其中第 j 个字符表示 pi,j。

接下来 q 行,每行四个正整数x1,y1,x2,y2 (1≤x1≤x2≤n, 1≤y1≤y2≤n),依次表示每次尝试的矩形的对角线的端点坐标。

Output

输出 q 行,第 i 行输出一个正整数,表示第 i 次尝试对应的最小单位纹样包含的像素点数量。

思路:

对于一个询问区间,横着和竖着的周期性互不干扰,所以可以分别求其最小周期,然后相乘。

对于横着的最小周期,我们可以把纵向维度压扁,转化为一个一维问题。压扁的时候可以用字符串哈希,预处理出哈希值的前缀和,询问的时候把区间内的每一列压成一个哈希值。

转化为一维问题后,可以再用一次哈希,求出最大公共前后缀,用区间长减去这个最大值即最小周期。(也可用kmp处理)

注意两次hash的P最好不相同。(相同会WA,原因不明呜呜)

#include<bits/stdc++.h>
using namespace std;

#define maxn 2000
#define maxQ 10000
#define ull unsigned long long


string a[maxn+5];

const int P=131,_P=1331;
ull p[maxn+5],_p[maxn+5];
ull hashrow[maxn+5][maxn+5],hashcol[maxn+5][maxn+5];

ull drow[maxn+5],dcol[maxn+5];

int main() {
	
	int n,Q;
	cin>>n>>Q;
	for(int i=1;i<=n;i++) {
		cin>>a[i];
		a[i]=" "+a[i];
	}
	
	p[0]=_p[0]=1;
	for(int i=1;i<=n;i++) p[i]=p[i-1]*P,_p[i]=_p[i-1]*_P;
	
	for(int i=1;i<=n;i++) {
		for(int j=1;j<=n;j++) {
			hashrow[i][j]=hashrow[i-1][j]*P+a[i][j]-'a';
			hashcol[i][j]=hashcol[i][j-1]*P+a[i][j]-'a';
		}
	}
	
	while(Q--) {
		int x1,y1,x2,y2;
		cin>>x1>>y1>>x2>>y2;
		
		for(int j=y1;j<=y2;j++) 
			drow[j]=hashrow[x2][j]-hashrow[x1-1][j]*p[x2-x1+1];
		for(int i=x1;i<=x2;i++) 
			dcol[i]=hashcol[i][y2]-hashcol[i][y1-1]*p[y2-y1+1];
		
		ull s1=0,s2=0,ans1=-1,ans2=-1;
		for(int j=0;y1+j<y2;j++) {
			s1=s1*_P+drow[y1+j];
			s2=drow[y2-j]*_p[j]+s2;
			if(s1==s2) {
				ans1=j;
			}
		}
		s1=s2=0;
		for(int i=0;x1+i<x2;i++) {
			s1=s1*_P+dcol[x1+i];
			s2=dcol[x2-i]*_p[i]+s2;
			if(s1==s2) {
				ans2=i;
			}
		}
		
		cout<<(y2-y1-ans1)*(x2-x1-ans2)<<endl;		
	}
	
	return 0;
}



版权声明:本文为rabbit_ZAR原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。