CF1500D Tiles for Bathroom

一开始想的是顺序枚举左上角,然后算出以(x,y)为左上角的最小右下角答案。然而下一步直接死掉,根本没得做。他不像CF1396D点的个数只有2000,可以扫两维,每次移动扫描线只是单点修改。这个不行。
然后瞄了一眼题解发现这玩意其实有f(x,y)f(x,y1)1,就是考虑包含关系。但是暴力扩展我依然不会做。就是说从一个位置扩展到新的位置,新的东西永远都是没处理过的,或者没法预处理的。
枚举左上角会有这种问题,那改成右下角就不一样了,(顺序枚举右下角等于换种顺序枚举另外3个角)因为枚举到(i,j)的时候,没处理过的点只有(i,j)一个。然后冷静考虑枚举右下角需要维护什么,就是求最大的左上角。
考虑一维的情况怎么处理,就是枚举右端点,求上一个和他之间有q种颜色的位置。可以考虑维护q+1个下标表示离他最近的q+1个颜色最后出现的位置。每向右挪一个,都会往里考虑加一个点,直接插入或者替换一个即可,都是插在队尾。
二维情况类似,右端点->右下角,下标->横纵坐标,最近距离->最近切比雪夫距离。别的一样。
而且要注意是从f(i1,j1)和两条转移过来。只有从f(i1,j1)f(i,j)的时候,队列里的点到当前右下角的切比雪夫距离的相对大小关系是不变的。归并一下即可O(n2q)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<vector>
#include<map>
using namespace std;
typedef long long ll;
#define N 1504
struct node{int x,y;}c[N][N][13],cc[13];
int n,q,a[N][N],le[N][N][13],up[N][N][13],tc[13],tl[N][N],tu[N][N],tot[N][N];
bool vis[N*N];
int ans[N];
inline void rd(int &x)
{
    x=0;register char c=getchar();
    while(c<'0'||c>'9')c=getchar();
    while(c>='0'&&c<='9'){x=x*10+c-48;c=getchar();}
}
int main()
{
	rd(n),rd(q);
    for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)rd(a[i][j]);
    for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)
    {
        for(int ii=1;ii<=tl[i][j-1];ii++)tc[ii]=le[i][j-1][ii];
        tl[i][j]=tl[i][j-1];bool flag=false;
        for(int ii=1;ii<=tl[i][j];ii++)if(a[i][tc[ii]]==a[i][j])
        {
            for(int jj=ii;jj<tl[i][j];jj++)tc[jj]=tc[jj+1];
            tc[tl[i][j]]=j;flag=true;break;
        }
        if(!flag)
        {
            if(tl[i][j]==q+1){for(int ii=1;ii<tl[i][j];ii++)tc[ii]=tc[ii+1];tl[i][j]--;}
            tc[++tl[i][j]]=j;
        }for(int ii=1;ii<=tl[i][j];ii++)le[i][j][ii]=tc[ii];
    }
    for(int j=1;j<=n;j++)for(int i=1;i<=n;i++)
    {
        for(int ii=1;ii<=tu[i-1][j];ii++)tc[ii]=up[i-1][j][ii];
        tu[i][j]=tu[i-1][j];bool flag=false;
        for(int ii=1;ii<=tu[i][j];ii++)if(a[tc[ii]][j]==a[i][j])
        {
            for(int jj=ii;jj<tu[i][j];jj++)tc[jj]=tc[jj+1];
            tc[tu[i][j]]=i;flag=true;break;
        }
        if(!flag)
        {
            if(tu[i][j]==q+1){for(int ii=1;ii<tu[i][j];ii++)tc[ii]=tc[ii+1];tu[i][j]--;}
            tc[++tu[i][j]]=i;
        }for(int ii=1;ii<=tu[i][j];ii++)up[i][j][ii]=tc[ii];
    }
    for(int i=1;i<=n;i++)
    {tot[1][i]=tl[1][i];
        for(int ii=1;ii<=tl[1][i];ii++)c[1][i][ii]=(node){1,le[1][i][ii]};
        ans[1]++;
    }
    for(int i=2;i<=n;i++)
    {tot[i][1]=tu[i][1];
        for(int ii=1;ii<=tu[i][1];ii++)c[i][1][ii]=(node){up[i][1][ii],1};
        ans[1]++;
    }
    for(int i=2;i<=n;i++)for(int j=2;j<=n;j++)
    {
        int z1=tot[i-1][j-1],z2=tu[i][j],z=1;
        while(z1&&z2&&z<=q+1)
        {
            if(vis[a[c[i-1][j-1][z1].x][c[i-1][j-1][z1].y]]){z1--;continue;}
            if(vis[a[up[i][j][z2]][j]]){z2--;continue;}
            int d1=max(i-c[i-1][j-1][z1].x,j-c[i-1][j-1][z1].y);
            int d2=i-up[i][j][z2];
            if(d1<d2)cc[z++]=c[i-1][j-1][z1--];
            else cc[z++]=(node){up[i][j][z2--],j};
            vis[a[cc[z-1].x][cc[z-1].y]]=1;
        }
        while(z1&&z<=q+1)
        {
        	if(vis[a[c[i-1][j-1][z1].x][c[i-1][j-1][z1].y]]){z1--;continue;}
        	cc[z++]=c[i-1][j-1][z1--];
        	vis[a[cc[z-1].x][cc[z-1].y]]=1;
		}
		while(z2&&z<=q+1)
		{
			if(vis[a[up[i][j][z2]][j]]){z2--;continue;}
			cc[z++]=(node){up[i][j][z2--],j};
			vis[a[cc[z-1].x][cc[z-1].y]]=1;
		}
        tot[i][j]=z-1;
        for(int ii=1;ii<z;ii++)c[i][j][ii]=cc[ii];
        for(int ii=1;ii<z;ii++)vis[a[c[i][j][ii].x][c[i][j][ii].y]]=0;
        reverse(c[i][j]+1,c[i][j]+tot[i][j]+1);
        z1=tot[i][j],z2=tl[i][j-1],z=1;
        while(z1&&z2&&z<=q+1)
        {
            if(vis[a[c[i][j][z1].x][c[i][j][z1].y]]){z1--;continue;}
            if(vis[a[i][le[i][j-1][z2]]]){z2--;continue;}
            int d1=max(i-c[i][j][z1].x,j-c[i][j][z1].y);
            int d2=j-le[i][j-1][z2];
            if(d1<d2)cc[z++]=c[i][j][z1--];
            else cc[z++]=(node){i,le[i][j-1][z2--]};
            vis[a[cc[z-1].x][cc[z-1].y]]=1;
        }
        while(z1&&z<=q+1)
        {
        	if(vis[a[c[i][j][z1].x][c[i][j][z1].y]]){z1--;continue;}
        	cc[z++]=c[i][j][z1--];
        	vis[a[cc[z-1].x][cc[z-1].y]]=1;
		}
		while(z2&&z<=q+1)
		{
			if(vis[a[i][le[i][j-1][z2]]]){z2--;continue;}
			cc[z++]=(node){i,le[i][j-1][z2--]};
			vis[a[cc[z-1].x][cc[z-1].y]]=1;
		}
        tot[i][j]=z-1;
        for(int ii=1;ii<z;ii++)c[i][j][ii]=cc[ii];
        reverse(c[i][j]+1,c[i][j]+1+tot[i][j]);
        for(int ii=1;ii<z;ii++)vis[a[c[i][j][ii].x][c[i][j][ii].y]]=0;
        //printf("%d %d:\n",i,j);
        //for(int ii=1;ii<=tot[i][j];ii++)printf("(%d %d)",c[i][j][ii].x,c[i][j][ii].y);puts("");
        if(tot[i][j]<=q)ans[min(i,j)]++;
        else
        {
            int te=max(i-c[i][j][1].x,j-c[i][j][1].y);
            ans[min(min(i,j),te)]++;
        }
    }
    for(int i=n;i;i--)ans[i]+=ans[i+1];
    for(int i=1;i<=n;i++)printf("%d\n",ans[i]);
}
posted @   Lebron_Durant  阅读(202)  评论(2编辑  收藏  举报
编辑推荐:
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
点击右上角即可分享
微信分享提示