CF1446E Long Recovery

27 篇文章 0 订阅
16 篇文章 0 订阅

Description

Solution

  • 玩了一段时间觉得可以直接模拟,全部涂成黑色,然后再全部变成白色,BFS即可???
  • 好吧,除了样例什么都过不去。
  • 因为有一种特殊的情况:并不一定要先补上那个空白,而是可以先右上的删去,再加入空白,再把右上加入,可以白嫖一次。

在这里插入图片描述

  • 那么我们只需要把这种情况判断掉就好了!!!好吧,到底应该怎么判,答案怎么计算还有重新想一想。

  • 我们可以从中定义一个势能 ϕ \phi ϕ,即相邻的块不同的个数,那么每一次翻转一个格子会根据旁边有三个不同还是两个不同而使得 ϕ \phi ϕ减去 3 3 3 1 1 1,显然如果可以做完的话,这个 ϕ \phi ϕ对应也会减到 0 0 0,我们想让次数最多,因此要让 − 3 -3 3的操作尽量少。

  • 首先可以用之前naive的BFS把尽量染黑的最终状态求出来,假设连通块中有环,那么就是 S I C K SICK SICK

  • 接下来对于每一个连通块单独考虑,首先最后一次一定是 − 3 -3 3的操作,接下来我们可以证明如果这个连通块初始时不是这样,就一定不需要额外的 − 3 -3 3操作:
    在这里插入图片描述

  • 证明考虑如果有一个角的周围有 1 1 1个格子有颜色,那么可以用开头提到的方法避免直接操作中间导致 ϕ − 3 \phi-3 ϕ3,否则每一个角周围都有两个颜色,那么一定是这样:
    在这里插入图片描述

  • 会形成一个环,矛盾了。那么就只有每一个角周围都没有颜色时,需要花费 − 3 -3 3

  • 因此最后只需要计算一下 ϕ \phi ϕ,连通块个数,孤立的三角形的个数即可。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define maxn 520
#define maxd 20000005
using namespace std;

const int fx[2][3][2]={{{1,0},{-1,0},{1,-1}},{{1,0},{-1,0},{-1,1}}};
int n,i,j,k,cnt[maxn][maxn],d[maxd][2],bz[maxn][maxn],ans,a[maxn][maxn],vis[maxn][maxn];

void dfs(int x,int y,int px,int py){
	vis[x][y]=1;
	for(int k=0;k<3;k++){
		int xx=x+fx[x&1][k][0],yy=y+fx[x&1][k][1];
		if (xx>=0&&yy>=0&&(xx!=px||yy!=py)) if (bz[xx][yy]){
			if (vis[xx][yy]){
				printf("SICK\n");
				exit(0);
			}
			dfs(xx,yy,x,y);
		}
	}
}

int check(int x,int y){
	for(int k=0;k<3;k++){
		int xx=x+fx[x&1][k][0],yy=y+fx[x&1][k][1];
		if (xx<0||yy<0) return 0;
		if (!bz[xx][yy]||!a[xx][yy]) return 0;
		for(int kk=0;kk<3;kk++){
			int xx0=xx+fx[xx&1][kk][0],yy0=yy+fx[xx&1][kk][1];
			if (xx0>=0&&yy0>=0&&(xx0!=x||yy0!=y)){
				if (bz[xx0][yy0])
					return 0;
			}
		}
	}
	return 1;
}

int main(){
	freopen("ceshi.in","r",stdin);
	freopen("ceshi1.out","w",stdout);
	scanf("%d",&n);
	int t=0,w=0;
	for(i=1;i<=n;i++){
		scanf("%d%d",&j,&k),j+=4,k+=2,a[j][k]=1;
		bz[j][k]=1,w++,d[w][0]=j,d[w][1]=k;
	}
	for(int x=0;x<maxn;x++) for(int y=0;y<maxn;y++) if (bz[x][y]){
		for(k=0;k<3;k++){
			int xx=x+fx[x&1][k][0],yy=y+fx[x&1][k][1];
			if (xx>=0&&yy>=0) 
				ans+=bz[x][y]!=bz[xx][yy];
		}
	}
	while (t<w){
		t++; int x=d[t][0],y=d[t][1];
		for(k=0;k<3;k++){
			int xx=x+fx[x&1][k][0],yy=y+fx[x&1][k][1];
			if (xx>=0&&yy>=0){
				cnt[xx][yy]++;
				if (!bz[xx][yy]&&cnt[xx][yy]>=2){
					bz[xx][yy]=1;
					w++,d[w][0]=xx,d[w][1]=yy;
				}
			}
		}
	}
	for(i=0;i<maxn;i++) for(j=0;j<maxn;j++) if (!vis[i][j]&&bz[i][j])
		dfs(i,j,-1,-1),ans-=2;
	for(i=0;i<maxn;i++) for(j=0;j<maxn;j++) if (bz[i][j]&&!a[i][j])
		ans-=check(i,j)*2;
	printf("RECOVERED\n");
	printf("%d\n",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值