JZOJ1332. 正方形内的计数

本文介绍了一道算法题目,目标是在有限的火柴棍条件下,通过放置一根火柴增加正方形的数量。文章详细解析了如何通过枚举对角线的方法来解决该问题,并提供了完整的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

爱偷懒的小X这次玩起了一个幼稚的游戏。他拿出若干相同长度的火柴棍,在地上摆了起来。摆着摆着小X发现火柴不够,不能按原先的设想摆出一个N×N的正方形网格。只剩下最后一根火柴了,于是小X把这根火柴交给了LHX教主。小X只有一个要求,就是在现有的基础上能让这个不怎么好看的网格再多出至少一个正方形(你可以认为一个2×2完整网格有5个正方形)。   

但是LHX教主为了显示他足够NB,他想把所有满足要求的位置都告诉小X。现在假如你就是LHX教主,你只需要输出有多少个位置放下一根火柴后可以让整个网格多出至少一个正方形。

题解

遇到正方形,显然想到枚举对角线。
我们可以先预处理出来每一个位置向上下左右的长度,
在同一条对角线上任意两点都可以构成一个正方形。
通过之前预处理出来的长度,我们可以知道一个点它向左和向下的边长,
然后在这个边长空一个位置,可以找到另一个边长。
那么夹在两个边长中如果有点就可以通过补这个位置得到新的正方形。

code

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define N 2003
using namespace std;
bool bz[N][N][2];
char ch;
int n,ans,len,t,x,y;
bool p[N][N][2];
int up[N][N],down[N][N],le[N][N],ri[N][N],del[N],b[N],next[N],tot;
int tr[N];
int find(int x)
{
    int s=0;
    if(x<=0)return 0;
    for(;x;x=x-(x&(-x)))
        s+=tr[x];
    return s;
}
void add(int x,int z)
{
    if(x<=0)return;
    for(;x<=n+1;x=x+(x&(-x)))
        tr[x]+=z;
} 
void work(int x,int y)
{
    memset(tr,0,sizeof(tr));
    memset(del,0,sizeof(del));
    memset(next,0,sizeof(next));
    memset(b,0,sizeof(b));
    for(tot=1;x<=n+1 && y>0;x++,y--,tot++)
    {
        for(int k=b[x];k;k=next[k])add(del[k],-1);
        if(up[x][y]<ri[x][y])
        {
            len=up[x][y];
            t=min(ri[x][y],up[x-len-1][y]+len+1);
            if(find(x-len-1)-find(x-t-1)>0)
            {
                if(p[x-len-1][y][1])ans++;
                p[x-len-1][y][1]=0;
            }
        }
        else
        {
            len=ri[x][y];
            t=min(up[x][y],ri[x][y+len+1]+len+1);
            if(find(x-len-1)-find(x-t-1)>0)
            {
                if(p[x][y+len][0])ans++;
                p[x][y+len][0]=0;
            }
        }
        add(x,1);
        len=min(down[x][y],le[x][y])+1;
        next[tot]=b[x+len];
        del[tot]=x;
        b[x+len]=tot;
        //add(x+len,-1);
        //for(int k=x;k<x+len;k++)add(k,1);
    }
}


void work1(int x,int y)
{
    memset(tr,0,sizeof(tr));
    memset(del,0,sizeof(del));
    memset(next,0,sizeof(next));
    memset(b,0,sizeof(b));
    for(tot=1;x>0 && y<=n+1;x--,y++,tot++)
    {
        for(int k=b[x];k;k=next[k])add(del[k],-1);
        if(le[x][y]<down[x][y])
        {
            len=le[x][y];
            t=min(down[x][y],le[x][y-len-1]+len+1);
            if(find(x+t)-find(x+len)>0)
            {
                if(p[x][y-len-1][0])ans++;
                p[x][y-len-1][0]=0;
            }
        }
        else
        {
            len=down[x][y];
            t=min(le[x][y],down[x+len+1][y]+len+1);
            if(find(x+t)-find(x+len)>0)
            {
                if(p[x+len][y][1])ans++;
                p[x+len][y][1]=0;
            }
        }
        add(x,1);
        len=min(up[x][y],ri[x][y]);
        next[tot]=b[x-len-1];
        del[tot]=x;
        b[x-len-1]=tot;
        //add(x+len,-1);
        //for(int k=x;k<x+len;k++)add(k,1);
    }
}


int main()
{
    freopen("data10.in","r",stdin);
    scanf("%d",&n);
    memset(bz,0,sizeof(bz));
    memset(p,1,sizeof(p));
    ch=getchar();
    for(int i=1;i<=n+1;i++)
    {
        ch=getchar();
        while(ch!='_' && ch!='|' && ch!=' ')ch=getchar();
        for(int j=1;j<=2*n+1;j++)
        {
            //printf("%c",ch);
            if(ch=='_')bz[i][j/2+1][0]=1;
            if(ch=='|')bz[i][j/2+1][1]=1;
            ch=getchar();
        }
        //printf("\n");
    }
    for(int i=1;i<=n+1;i++)
        for(int j=1;j<=n+1;j++)
        {
            if(bz[i][j][0])le[i][j]=le[i][j-1]+1;
            if(bz[i][j][1])up[i][j]=up[i-1][j]+1;
        }
    for(int i=n+1;i;i--)
        for(int j=n+1;j;j--)
        {
            if(bz[i][j+1][0])ri[i][j]=ri[i][j+1]+1;
            if(bz[i+1][j][1])down[i][j]=down[i+1][j]+1; 
        }
    for(int i=1;i<=n+1;i++)
        work(1,i),work1(n+1,i);
    for(int i=1;i<=n+1;i++)
        work(i,n+1),work1(i,1); 

    printf("%d",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值