CF750H New Year and Snowy Grid

该问题讨论了一个关于棋盘的路径连通性判断。给定一个有障碍的棋盘,需要在每次添加新障碍后判断是否存在两条不相交的路径从左上角到右下角。关键在于判断最外层是否八连通以及在添加障碍后的连通性。算法使用了可撤销并查集,时间复杂度为O(hw+qk(k+logk))。

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

题面描述

给一个 h × w h\times w h×w 的棋盘,有障碍。 q q q 次询问,每次把 k k k 个点变成障碍后询问能否找到两条从 ( 1 , 1 ) (1,1) (1,1) ( h , w ) (h,w) (h,w) 的不相交的路径,询问独立。需要通过交互实现强制在线。
h , w ≤ 1 0 3 h, w \leq 10^3 h,w103 q ≤ 1 0 4 q \leq 10^4 q104 k ≤ 10 k \leq 10 k10

题解

非常玄妙的结论题
考虑判断 ( 1 , 1 ) (1,1) (1,1) ( h , w ) (h,w) (h,w)是否连通如何解决
在最外圈除了起点和终点附近的地方都看成 # \# #
在这里插入图片描述
图片来源
类似上图
条件就是 ( h + 1 , 0 ) (h+1,0) (h+1,0) ( 0 , w + 1 ) (0,w+1) (0,w+1)是否八连通
然后拓展到判断是否有两条不交路径,条件就是判断 ( h + 1 , 0 ) (h+1,0) (h+1,0) ( 0 , w + 1 ) (0,w+1) (0,w+1)是否八连通或者在加入一个 # \# #后是否可以八连通

加入 k k k # \# #后怎么判断
1. 1. 1.直接连通
2. 2. 2. k k k # \# #周围加一个 # \# #可以达成八连通,可以枚举一下与 k k k # \# #距离为 2 2 2的位置
3. 3. 3. k k k # \# #会接触到的连通块只有 O ( k ) O(k) O(k)个,找出来互相判断一下能否通过加入一个 # \# #使两个连通块连通,并且判断一下两个连通块是否一个连着 ( h + 1 , 0 ) (h+1,0) (h+1,0),另一个连着 ( 0 , w + 1 ) (0,w+1) (0,w+1)
判断两个连通块能否通过加入一个 # \# #使两个连通块连通,可以通过预处理所有这样的连通块对,因为每个格子只会产生 O ( 1 ) O(1) O(1)个连通块对,所以最多 O ( h w ) O(hw) O(hw)个连通块对,用哈希表存下,就可以 O ( 1 ) O(1) O(1)回答询问
连通性可以用可撤销并查集
时间复杂度 O ( h w + q k ( k + log ⁡ k ) ) O(hw+qk(k+\log k)) O(hw+qk(k+logk))

code \text{code} code

代码写得很长

#include<cstdio>
#include<algorithm>
#define ull unsigned long long
using namespace std;
void read(int &res)
{
	res=0;char ch=getchar();
	while(ch<'0'||ch>'9') ch=getchar();
	while('0'<=ch&&ch<='9') res=(res<<1)+(res<<3)+(ch^48),ch=getchar();
}
void readc(char &ch)
{
	ch=getchar();
	while(ch!='#'&&ch!='.') ch=getchar();
}
const int N=1010;
const int dx[8]={-1,-1,-1,0,0,1,1,1};
const int dy[8]={-1,0,1,-1,1,-1,0,1};
const int dex[16]={-2,-2,-2,-2,-2,-1,-1,0,0,1,1,2,2,2,2,2};
const int dey[16]={-2,-1,0,1,2,-2,2,-2,2,-2,2,-2,-1,0,1,2};
bool vis[N+10][N+10];
int h,w,q;
int id(int x,int y){return x*(w+2)+y+1;}
int fa[N*N+10],g[N*N+10],x[N+10],y[N+10];
vector<pair<int,int> > p;
int findfa(int x)
{
	if(fa[x]==x) return x;
	else
	{
		p.push_back({x,fa[x]});
		return fa[x]=findfa(fa[x]);
	}
}
void merge(int x,int y)
{
	x=findfa(x),y=findfa(y);
	if(x!=y)
	{
		p.push_back({x,fa[x]});
		fa[x]=y;
	}
}
int findfa1(int x)
{
	if(fa[x]==x) return x;
	else return fa[x]=findfa1(fa[x]);
}
void merge1(int x,int y)
{
	x=findfa1(x),y=findfa1(y);
	if(x!=y) fa[x]=y;
}
const ull mod=999979;
struct hash_table
{
	int st[mod+10],tot;
	struct edge
	{
		ull key;int last;
	}e[N*N*4];
	void insert(ull key)
	{
		ull h=key%mod;
		for(int i=st[h];i!=0;i=e[i].last)
			if(e[i].key==key)
				return;
		e[++tot].key=key;
		e[tot].last=st[h];
		st[h]=tot;
	}
	bool query(ull key)
	{
		ull h=key%mod;
		for(int i=st[h];i!=0;i=e[i].last)
			if(e[i].key==key)
				return true;
		return false;
	}
}t;
int main()
{
//	freopen("a.in","r",stdin);
	read(h),read(w),read(q);
	for(int i=1;i<=h;i++)
		for(int j=1;j<=w;j++)
		{
			char ch;readc(ch);
			vis[i][j]=(ch=='.');
		}
	vis[0][0]=vis[0][1]=vis[1][0]=vis[h+1][w+1]=vis[h][w+1]=vis[h+1][w]=true;
	for(int i=0;i<=h+1;i++)
		for(int j=0;j<=w+1;j++)
			fa[id(i,j)]=id(i,j);
	for(int i=0;i<=h+1;i++)
		for(int j=0;j<=w+1;j++)
		{
			if(vis[i][j]) continue;
			if(i>0&&!vis[i-1][j]) merge1(id(i,j),id(i-1,j));
			if(j>0&&!vis[i][j-1]) merge1(id(i,j),id(i,j-1));
			if(i>0&&j>0&&!vis[i-1][j-1]) merge1(id(i,j),id(i-1,j-1));
			if(i>0&&j<w+1&&!vis[i-1][j+1]) merge1(id(i,j),id(i-1,j+1));
		}
	for(int i=0;i<=h+1;i++)
		for(int j=0;j<=w+1;j++)
			fa[id(i,j)]=findfa1(id(i,j));
	for(int i=1;i<=h;i++)
		for(int j=1;j<=w;j++)
		{
			if(!vis[i][j]) continue;
			if(i==1&&j==1||i==h&&j==w) continue;
			g[0]=0;
			for(int d=0;d<8;d++)
				if(!vis[i+dx[d]][j+dy[d]])
					g[++g[0]]=fa[id(i+dx[d],j+dy[d])];
			for(int k=1;k<=g[0];k++)
				for(int w=k+1;w<=g[0];w++)
					if(g[k]!=g[w])
						t.insert((ull)N*N*g[k]+g[w]),t.insert((ull)N*N*g[w]+g[k]);
		}
	if(fa[id(h+1,0)]==fa[id(0,w+1)])
	{
		for(;q--;)
		{
			puts("NO\n");
			fflush(stdout);
		}
		return 0;
	}
	for(int k;q--;)
	{
		read(k);
		for(int i=1;i<=k;i++) read(x[i]),read(y[i]);
		g[0]=0,g[++g[0]]=fa[id(h+1,0)],g[++g[0]]=fa[id(0,w+1)];
		for(int i=1;i<=k;i++)
			for(int d=0;d<8;d++)
				if(!vis[x[i]+dx[d]][y[i]+dy[d]])
					g[++g[0]]=fa[id(x[i]+dx[d],y[i]+dy[d])];
		for(int i=1;i<=k;i++)
		{
			vis[x[i]][y[i]]=false;
			for(int d=0;d<8;d++)
				if(!vis[x[i]+dx[d]][y[i]+dy[d]])
					merge(id(x[i],y[i]),id(x[i]+dx[d],y[i]+dy[d]));
		}
		bool ans=true;
		if(findfa(id(h+1,0))==findfa(id(0,w+1))) ans=false;
		else
		{
			for(int i=1;i<=k;i++)
			{
				for(int d=0;d<16;d++)
				{
					int Dx=x[i]+dex[d],Dy=y[i]+dey[d];
					if(0<=Dx&&Dx<=h+1&&0<=Dy&&Dy<=w+1&&!vis[Dx][Dy])
					{
						if(findfa(id(h+1,0))==findfa(id(Dx,Dy))&&findfa(id(0,w+1))==findfa(id(x[i],y[i]))
						||findfa(id(h+1,0))==findfa(id(x[i],y[i]))&&findfa(id(0,w+1))==findfa(id(Dx,Dy)))
						{
							ans=false;
							break;
						}
					}
				}
				if(!ans) break;
			}
			if(ans)
			{
				for(int i=1;i<=g[0];i++)
				{
					for(int j=1;j<=g[0];j++)
						if(g[i]!=g[j])
						{
							if(t.query((ull)N*N*g[i]+g[j]))
								if(findfa(id(h+1,0))==findfa(g[i])&&findfa(id(0,w+1))==findfa(g[j]))
								{
									ans=false;
									break;
								}
						}
					if(!ans) break;
				}
			}
		}
		if(!ans) puts("NO");
		else puts("YES");
		fflush(stdout);
		for(int i=1;i<=k;i++) vis[x[i]][y[i]]=true;
		reverse(p.begin(),p.end());
		for(auto v:p) fa[v.first]=v.second;
		p.clear();
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值