4473: [Jsoi2015]symmetry

4473: [Jsoi2015]symmetry

Time Limit: 10 Sec   Memory Limit: 128 MB
Submit: 22   Solved: 8
[ Submit][ Status][ Discuss]

Description

张老师正在课堂上讲解正方形的对称模式。为了简单起见,他用0-1方阵代表一个正方
形图形,即把正方形分解成n×n的方格阵,0代表一个白色方格,1代表一个黑色方格。
首先讲到的是容易辨别的轴对称,正方形有4个对称轴,分别是水平中线,竖直中线和两
条对角线。如果一个正方形图形以某个对称轴做反射变换后保持不变,就称为轴对称图形。
例如下面的两个图形都是轴对称图形。
100010
100101
111000
张老师继续讲解正方形的旋转对称。如果一个正方形图形以中心点旋转180度后保持
不变,就称为180度对称图形。如果以中心点顺时针旋转90度后保持不变,就称为90度
对称图形,例如下面的两个图形左边是180度对称图形,右边是90度对称图形。
00111011
11101110
01110111
11001101
张老师接着说,如果一个正方形图形具有两个互相垂直的对称轴,就称为4对称图形,
如果关于4个对称轴全部对称,就称为8对称图形。按照定义,90度对称图形也是180度
对称图形,8对称图形也是4对称图形。当正方形图形的边长为偶数时,该图形的中心是最
中间4个方格的公共顶点,当正方形图形的边长为奇数时,该图形有一个中心方格,该图
形的中心也是它的中心方格的中心。边长为1的图形显然是8对称图形。
张老师给学生证明了两个定理。
定理1:一个正方形图形是4对称图形当且仅对它是180度对称图形和轴对称图形。
定理2:一个正方形图形是8对称图形当且仅对它是90度对称图形和轴对称图形。
最后是练习时间,张老师要求学生寻找在大正方形图形中出现的各种对称图形。请你编
程实现这个要求。设输入一个0-1方阵,输出满足8对称,90度对称,4对称,180度对称
和轴对称的最大子连续方阵的边长。子连续方阵是指选择若干相邻行列的子方阵,代表在大
图形中出现的小图形。

Input

输入的第一行是一个正整数n(5<=n<=500),表示大正方形图形的边长。然后是n行长度为n的01
字符串

Output

输出有一行,包含5个以空格隔开的自然数,分别表示在输入中出现的最大8对称,
90度对称,4对称,180度对称和轴对称的子连续方阵的行数。

Sample Input

5
11100
11000
10111
11000
11100

Sample Output

2 2 3 3 5
【样例说明】
大图形有水平对称轴,左上角有一个2×2的8对称图形,中间3行最右3列构成4对
称图形。

HINT

Source

[ Submit][ Status][ Discuss]




维护七个矩形,分别是原矩形,四个对称,旋转90°和180°

对于这七个矩形做一次二维hash,论文传送门

这里就不讲怎么二维hash啦。。

现在枚举每一个矩形,它的各种变换总能在维护的其他矩形中找到,画个图就很好推坐标了

如果直接暴力枚举是O(n^3)的,n = 500常数略大

注意到不管那一种矩形总是有奇偶分开的单调

即如果k是某个询问的合法答案,那么k-2一定合法(k>2)

这样把最外层用二分搞掉

O(n^2logn)



一开始没想到只需维护大矩形的hash就行,每次询问长度时都重新做一次hash,常数大爆炸

最后通过读std顺便了解了template的用法--

#include<iostream>
#include<cstdio>
#include<queue>
#include<vector>
#include<bitset>
#include<algorithm>
#include<cstring>
#include<map>
#include<stack>
#include<set>
#include<cmath>
#include<ext/pb_ds/priority_queue.hpp>
using namespace std;
 
const int maxn = 505;  
typedef unsigned long long UL;  
const UL p = 3246307;  
const UL q = 4596359;  
 
UL h1[maxn][maxn],h2[maxn][maxn],h3[maxn][maxn],f[maxn][maxn],
   h4[maxn][maxn],h5[maxn][maxn],h6[maxn][maxn],h7[maxn][maxn],
   mip[maxn],miq[maxn];
 
int n,t1,t2,n1[maxn],n2[maxn];
char ch[maxn];
 
void Cal(UL (*h)[maxn])
{
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++) {
            UL A1 = h[i-1][j]*p;
            UL A2 = h[i][j-1]*q;
            UL A3 = h[i-1][j-1]*p*q;
            h[i][j] = A1 + A2 - A3 + h[i][j];
        }
}
 
void hash_pre()
{
    mip[0] = miq[0] = 1;
    for (int i = 1; i <= n; i++) {
        mip[i] = mip[i-1]*p;
        miq[i] = miq[i-1]*q;
    }
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++) {
            h1[i][j] = f[i][j];
            h2[j][i] = f[i][j];
            h3[n-j+1][n-i+1] = f[i][j];
            h4[n-i+1][j] = f[i][j];
            h5[i][n-j+1] = f[i][j];
            h6[j][n-i+1] = f[i][j];
            h7[n-i+1][n-j+1] = f[i][j];
        }
    Cal(h1); Cal(h2); Cal(h3);
    Cal(h4); Cal(h5); Cal(h6); Cal(h7);
}
 
bool Equal(UL (*h1)[maxn],int r1,int c1,UL (*h2)[maxn],int r2,int c2,int len)
{
    int x = r1 + len - 1;
    int y = c1 + len - 1;
    UL A1 = h1[r1-1][y]*mip[len];
    UL A2 = h1[x][c1-1]*miq[len];
    UL A3 = h1[r1-1][c1-1]*mip[len]*miq[len];
    UL T1 = h1[x][y] - A1 - A2 + A3;
    x = r2 + len - 1;
    y = c2 + len - 1;
    A1 = h2[r2-1][y]*mip[len];
    A2 = h2[x][c2-1]*miq[len];
    A3 = h2[r2-1][c2-1]*mip[len]*miq[len];
    UL T2 = h2[x][y] - A1 - A2 + A3;
    return T1 == T2;
}
 
bool pass_sym(int r,int c,int len)
{
    int x = r + len - 1;
    int y = c + len - 1;
    return Equal(h1,r,c,h2,c,r,len) || Equal(h1,r,c,h3,n-y+1,n-x+1,len)
           || Equal(h1,r,c,h4,n-x+1,c,len) || Equal(h1,r,c,h5,r,n-y+1,len);
}
 
bool pass_90(int r,int c,int len)
{
    int x = r + len - 1;
    int y = c + len - 1;
    return Equal(h1,r,c,h6,c,n-x+1,len);
}
 
bool pass_180(int r,int c,int len)
{
    int x = r + len - 1;
    int y = c + len - 1;
    return Equal(h1,r,c,h7,n-x+1,n-y+1,len);
}
 
bool check_8(int len)
{
    int tail = n - len + 1;
    for (int i = 1; i <= tail; i++)
        for (int j = 1; j <= tail; j++)
            if (pass_90(i,j,len) && pass_sym(i,j,len))
                return 1;
    return 0;
}
 
bool check_90(int len)
{
    int tail = n - len + 1;
    for (int i = 1; i <= tail; i++)
        for (int j = 1; j <= tail; j++)
            if (pass_90(i,j,len))
                return 1;
    return 0;
}
 
bool check_4(int len)
{
    int tail = n - len + 1;
    for (int i = 1; i <= tail; i++)
        for (int j = 1; j <= tail; j++)
            if (pass_180(i,j,len) && pass_sym(i,j,len))
                return 1;
    return 0;
}
 
bool check_180(int len)
{
    int tail = n - len + 1;
    for (int i = 1; i <= tail; i++)
        for (int j = 1; j <= tail; j++)
            if (pass_180(i,j,len))
                return 1;
    return 0;
}
 
bool check_sym(int len)
{
    if (!len || len > n) return 0;
    if (len == 1) return 1;
    int tail = n - len + 1;
    for (int i = 1; i <= tail; i++)
        for (int j = 1; j <= tail; j++)
            if (pass_sym(i,j,len))
                return 1;
    return 0;
}
 
template <class T>
int Solve(T check)
{
    int l = 1,r = t1,Ans;
    while (r - l > 1) {
        int mid = (l + r) >> 1;
        if (check(n1[mid])) l = mid;
        else r = mid;
    }
    if (check(n1[r])) Ans = n1[r];
    else Ans = n1[l];
    if (!check(Ans + 1)) return Ans;
    l = 1; r = t2;
    while (r - l > 1) {
        int mid = (l + r) >> 1;
        if (check(n2[mid])) l = mid;
        else r = mid;
    }
    if (check(n2[r])) return n2[r];
    else return n2[l];
}
 
int main()
{
    //freopen("4473.in","r",stdin);
    //freopen("4473.out","w",stdout);
     
    cin >> n;
    for (int i = 1; i <= n; i++) {
        scanf("%s",ch + 1);
        for (int j = 1; j <= n; j++)
            f[i][j] = ch[j] - '0';
    }
    hash_pre();
    for (int i = 1; i <= n; i += 2)  n1[++t1] = i;
    for (int i = 2; i <= n; i += 2) n2[++t2] = i;
    printf("%d ",Solve(check_8));
    printf("%d ",Solve(check_90));
    printf("%d ",Solve(check_4));
    printf("%d ",Solve(check_180));
    cout << Solve(check_sym);
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值