• 【题解】CF1015E Stars Drawing


    原题链接:点这里这里

    解法

    其实这题应该要缩短时间限制,不然感觉暴力都可以卡过去。

    这里提供一个 $O(nm cdotlog min(n,m))$ 的做法。

    首先 E1 的暴力做法比较好实现:

    • 枚举每一个 $'*'$ 的位置,并且尽可能向四个方向扩展,标记每一个扩展的位置。
    • 如果出现没有标记过的 $'*'$,输出 $-1$,否则输出刚刚扩展的方案。

    这个想法的时间复杂度是三次方级别的,我们应将其优化。

    如何快速定位每个位置的最大扩展长度

    我们可以先只考虑一行或一列的情况。

    如果我们把 $'*'$ 的值视作 $1$,其余视作 $0$,那么一段区间的价值和就是区间中 $'*'$ 的数量。

    所以,如果区间 $[l,r]$ 内全是 $'*'$ 而没有 $'.'$,那么该区间的价值和为 $r-l+1$。

    而我们扩展的时候,需要保证的是这个「星星图案」内没有 $'.'$ 。

    若从 $(i,j)$ 扩展长度 $k$,那么需要保证第 $i$ 行的第 $j-k~ sim j+k$ 列位置全部为 $'*'$,第 $j$ 列同理。

    上述条件,等价于第 $i$ 行的 $'*'$ 总数为 $j+k-(j-k)+1=2k+1$。

    如果不理解,可以看如下例子:

    ...*...
    ...*...
    ...*...
    *******
    ...*...
    ...*...
    ...*...

    这是一个大小为 $3$ 的「星星图案」。

    其中有「星星图案」的行、列的区间 $'*'$ 数均为 $2 imes3+1=7$。

    而如下这个图没有长度为 $3$「星星图案」。

    ...*...
    .......
    ...*...
    *******
    ...*...
    ...*...
    ...*...

    原因是若以 $(4,4)$ 为中心扩展 $3$ 格,那么第 $4$ 列的区间和为 $6$,与 $7$ 不相等。

    静态维护区间和,我们就可以使用前缀和

    只不过这是一个二维图案,我们需要分别对它进行行、列的前缀和。

    如何优化时间复杂度

    可以发现,如果以某点为中心,可以向外扩展 $k+1$ 个单位,那么它也绝对可以向外扩展 $k$ 个单位。

    因此,扩展的长度相对条件具有单调性,我们可以二分查找

    如何快速判断无解

    如果我们一个一个标记每个 $'*'$ 的位置是否访问过,那复杂度就退化了。

    但是我们有足够的复杂度可以在最后时刻一次性访问每个位置,判断它是否被访问过。

    所以,将整个区间进行标记,可以是用差分,最后用前缀和求得答案。

    注意一个「星星图案」的行、列都要标记。

    有人会有所担心行、列若分别标记一次,中心会被标记两次,影响答案。

    其实并不会,因为最后只有行、列的差分数组前缀和均为 $0$,才代表这个位置没有访问过。因此访问次数我们并不需要在意。

    以下是代码,不理解的地方可以参考注释

    #include <bits/stdc++.h>
    #define INF 1e9
    #define eps 1e-6
    typedef long long ll;
    using namespace std;
    
    int n, m, suml[1010][1010], sumu[1010][1010], l, r;
    int addl[1010][1010], addw[1010][1010], sl[1010][1010], sw[1010][1010];
    int ss, ans1[1000010], ans2[1000010], ans3[1000010];
    bool vis[1010][1010];
    char s[1010][1010];
    
    bool check(int i, int j, int x){    // 判断是否全为 *
        if(sumu[i + x][j] - sumu[i - x - 1][j] != x + x + 1) return 0;
        if(suml[i][j + x] - suml[i][j - x - 1] != x + x + 1) return 0;
        return 1;
    }
    
    int main(){
    
        cin >> n >> m;
        for(int i = 1; i <= n; i++)
            scanf("%s", s[i] + 1);
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= m; j++)    // 行和列分别前缀和
                suml[i][j] = suml[i][j - 1] + (s[i][j] == '*');
        for(int i = 1; i <= m; i++)
            for(int j = 1; j <= n; j++)
                sumu[j][i] = sumu[j - 1][i] + (s[j][i] == '*');
                
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= m; j++){
                // 扩展范围不能超出边界
                l = 0, r = min(i - 1, min(n - i, min(j - 1, m - j)));
                while(l < r){
                    int mid = (l + r + 1) >> 1;
                    if(check(i, j, mid)) l = mid;
                    else r = mid - 1;
                }
                if(l != 0){    // 记录答案、标记差分数组
                    ans1[++ss] = i, ans2[ss] = j, ans3[ss] = l;
                    addl[i - l][j]++, addl[i + l + 1][j]--;
                    addw[i][j - l]++, addw[i][j + l + 1]--;
                }
            }
        for(int i = 1; i <= n; i++)    // 将差分数组进行前缀和得到访问记录
            for(int j = 1; j <= m; j++)
                sw[i][j] = sw[i][j - 1] + addw[i][j];
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= m; j++)
                sl[i][j] = sl[i - 1][j] + addl[i][j];
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= m; j++){
                if(sw[i][j] == 0 && sl[i][j] == 0 && s[i][j] == '*'){    // 没有访问过的 * 需要输出 -1
                    puts("-1");
                    return 0;
                }
            }
        cout << ss << endl;    // 输出答案
        for(int i = 1; i <= ss; i++)
            printf("%d %d %d
    ", ans1[i], ans2[i], ans3[i]);
    
        return 0;
    }
  • 相关阅读:
    AJAX
    大前端面试一(基础)
    webpack打包vue -->简易讲解
    vue实现原理
    Spring boot 线上部署
    javascript 事件
    React native采坑路 Running 1 of 1 custom shell scripts
    PHP swoole实现redis订阅和发布
    JAVA 注解和反射
    微信公众平台获取用户地理位置之开发文档详解
  • 原文地址:https://www.cnblogs.com/zengpeichen/p/14947894.html
Copyright © 2020-2023  润新知