【题解】[COCI2020-2021#3] Sateliti

solution:

核心考点是字符串排序。码量较大。

总的情况显然最多有 n m nm nm 种,通过二维散列比较子矩阵可以达到 l o g n + l o g m logn+logm logn+logm ,所以直接暴力枚举 + 比较大小即可。难点在比较大小的方法,我们设 p [ i ] [ j ] = a [ i ] [ j ] ∗ 5 i 3 j p[i][j]=a[i][j]*5^i3^j p[i][j]=a[i][j]5i3j h a s ( i , j , i 2 , j 2 ) = ∑ l 1 = i i 2 ∑ l 2 = j j 2 p [ l 1 ] [ l 2 ] has(i,j,i2,j2)=\sum_{l1=i}^{i2}\sum_{l2=j}^{j2}p[l1][l2] has(i,j,i2,j2)=l1=ii2l2=jj2p[l1][l2]。这样前缀和可以方便地减去贡献。时间复杂度 O ( n m ( l o g n + l o g m ) ) O(nm(logn+logm)) O(nm(logn+logm))

#include <cstdio>
#include <cmath>
#include <algorithm>
#include <bitset>
#define PII pair<int,int>
#define ll long long
#define ull unsigned long long
using namespace std;
const int mx = 2005, P = 5, Q = 3;
int n, m;
char s[mx][mx];
ull p[mx][mx], h[mx][mx];
ull has(int i, int j, int i2, int j2) {
    return (h[i + i2][j + j2] - h[i][j + j2] - h[i + i2][j] + h[i][j]);
}
bool equal(int x, int y, int x2, int y2, int x3, int y3) {
    return has(x, y, x3, y3) * p[x2][y2] == has(x2, y2, x3, y3) * p[x][y];
}
int main() {
//      freopen("data.in","r",stdin);
    scanf("%d%d", &n, &m);

    for (int i = 0; i < n; i++) {
        scanf("%s", s[i]);
    }

    for (int i = 0; i < n << 1; i++) {
        for (int j = 0; j < m << 1; j++) {
            if (i == 0 && j == 0)
                p[i][j] = 1;
            else if (j == 0)
                p[i][j] = p[i - 1][j] * P;
            else {
                p[i][j] = p[i][j - 1] * Q;
            }
        }

    }

    for (int i = 0; i < n << 1; i++) {
        for (int j = 0; j < m << 1; j++) {
            h[i + 1][j + 1] = h[i][j + 1] + h[i + 1][j] - h[i][j] + (s[i % n][j % m]=='.') * p[i][j];
        }
    }

    int tx = 0, ty = 0;

    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            if (i == 0 && j == 0)
                continue;

            int l = 1, r = n, res = 0;

            while (l <= r) {
                int mid = l + r >> 1;

                if (equal(tx, ty, i, j, mid, m))
                    res = mid, l = mid + 1;
                else
                    r = mid - 1;
            }

            l = 1, r = m;
            int res2 = 0;

            while (l <= r) {
                int mid = l + r >> 1;

                if (equal(tx, ty, i, j, res + 1, mid))
                    res2 = mid, l = mid + 1;
                else
                    r = mid - 1;
            }

            if (res == n && res2 == m)
                continue;

            if (s[(i + res) % n][(j + res2) % m] < s[(tx + res) % n][(ty + res2) % m])
                tx = i, ty = j;
        }
    }

    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            printf("%c", s[(i + tx) % n][(j + ty) % m]);
        }

        printf("\n");
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值