• gmoj 6860. 【2020.11.14提高组模拟】鬼渊传说(village)


    题解


    subtask3,4

    枚举四边界再暴搜找连通块,时间复杂度 O( n6 )


    subtask5,6

    发现只有子矩阵内只有一个黑色格子才合法,所以枚举上下右边界,

    左边界范围考虑维护两个指针维护,时间复杂度 O( n3 )


    subtask7,8

    不妨把每个黑点当成点,相邻黑点间有条边,考虑欧拉公式 n + r = m + 2,即点数 + 面数 = 边数 + 2,

    由于不存在空腔所以 r( 面数 ) = 四元环( 四点四边的环 ) 个数 + 1,因此有  n + 四元环个数 - m = 1 把 点,横边,竖边,四元环找出来前缀和,

    枚举上下右边界,左边界用桶直接求 时间复杂度 O( n)


    subtask9,10

    发现如果一个子矩形里面存在空腔,则将其四边界扩展后空腔仍存在 bfs找出所有空腔,总数上限为 n2,在固定了上下右边界后,

    每个空腔是对左边界的限制 把空腔所在的子矩形(x1,y1,x2,y2)找出来,在上边界<=x1-1时在 (x2+1,y2+1) 处打上y1-1的限制,

    之后枚举下右边界时再处理出c[i]表示当前右边界为i的最大左边界,指针+桶单调维护左边界 时间复杂度 O( n3 )

    备注:设前缀和为 f [ i ] - f [ j ],要求 f [ i ] - f [ j ] = 1,即 f [ i ] - 1 = f [ j ],用桶维护 j 的个数,枚举 i 求和。

    #include<cstdio>
    #include<algorithm>
    #define ll long long
    using namespace std;
    
    const int N=305,M=N*N;
    struct zy{int x,y,u;}c[N*N];
    struct yz{int f,f1,g,g1,h,h1,l,l1;}f[N];
    int w[4][2]={{0,1},{1,0},{-1,0},{0,-1}};
    int a[N][N],bz[N][N],b[N][N],p[N],q[N],s[N*N*2];
    int n,m,i,j,l,r,u,d,t,cl,an,l1,l2;
    char ch[N];
    
    inline int max(int x,int y){return x>y ? x:y;}
    inline int min(int x,int y){return x<y ? x:y;}
    inline int cmp(zy x,zy y){return x.u<y.u;}
    inline int pd(int x,int y){return a[x][y]+a[x-1][y]+a[x][y-1]+a[x-1][y-1]==4;}
    inline int fj(int x){return f[x].f+f[x-1].g+f[x].h+f[x-1].l;}
    inline int fi(int x){return f[x].f+f[x].g+f[x].h+f[x].l;}
    void dfs(int x,int y){
        int xx,yy,o; bz[x][y]=1;
        l=min(l,y); r=max(r,y);
        u=min(u,x); d=max(d,x);
        for(o=0;o<=3;++o){
            xx=x+w[o][0]; yy=y+w[o][1];
            if (xx>=1&&xx<=n&&yy>=1&&yy<=m&&a[xx][yy]==0&&bz[xx][yy]==0)
                dfs(xx,yy);
        }
    }
    int main(){
        freopen("village.in","r",stdin);
        freopen("village.out","w",stdout);
        scanf("%d%d
    ",&n,&m);
        for(i=1;i<=n;++i){
            scanf("%s
    ",ch+1);
            for(j=1;j<=m;++j) a[i][j]=ch[j]-'0';
        }
        for(i=n;i>=1;--i)
            for(j=m;j>=1;--j)
                if (a[i][j]==0&&bz[i][j]==0){
                    l=u=N; r=d=0; dfs(i,j);
                    if (l==1||r==m||u==1||d==n||b[d+1][r+1]) continue;
                    b[d+1][r+1]=l-1; c[++t].u=u; c[t].x=d+1; c[t].y=r+1;
                }
        sort(c+1,c+t+1,cmp);
        cl=1;
        for(i=1;i<=n;++i){
            while(cl<=t&&i>=c[cl].u) b[c[cl].x][c[cl].y]=0,cl++;
            for(j=1;j<=m;++j){
                p[j]=0;
                f[j].f1=0; f[j].g1=a[i][j];
                f[j].l1=0; f[j].h1=-(a[i][j-1]+a[i][j]==2);
            }
            for(d=i;d<=n;++d){
                l=r=m;
                for(j=1;j<=m;++j){
                    p[j]=max(p[j],b[d][j]);
                    q[j]=max(q[j-1],p[j]);
                    f[j].f=f[j-1].f+f[j].f1;
                    f[j].g=f[j-1].g+f[j].g1;
                    f[j].l=f[j-1].l+f[j].l1;
                    f[j].h=f[j-1].h+f[j].h1;
                }
                for(j=m;j>=1;--j){
                    while(l>q[j]) s[fj(l)+M]++,l--;
                    while(r>l&&r>j) s[fj(r)+M]--,r--;
                    an+=s[fi(j)+M-1];
                }
                while(r>l) s[fj(r)+M]--,r--;
                if (d<n)
                for(j=1;j<=m;++j){
                    f[j].f1+=pd(d+1,j);
                    f[j].g1+=a[d+1][j];
                    f[j].l1-=a[d][j]+a[d+1][j]==2;
                    f[j].h1-=a[d+1][j-1]+a[d+1][j]==2;
                }
            }
        }
        printf("%d",an);
        return 0;
    }
  • 相关阅读:
    oracle闪回某个时间段的数据
    查询某个表某个字段重复记录急重复数量
    调用腾讯QQ启动
    MongoDB笔记(二):MongoDB下Shell的基本操作
    MongoDB笔记(一):MongoDB介绍及Windows下安装
    freemarker相关
    oracle获取时间毫秒数
    如何简单地理解Python中的if __name__ == '__main__'
    python套接字基本使用
    Mysql表的约束设计和关联关系设计
  • 原文地址:https://www.cnblogs.com/zsjz-yzy/p/13976105.html
Copyright © 2020-2023  润新知