CF232E Quick Tortoise

一、题目

点此看题

二、解法

考虑找中转点,也就是起点能到这个中转点并且中转点能到终点。

有一个重要的转化:要么不存在中转点,要么起点到终点的每一行都存在至少一个中转点。

那么以行为中心处理每个点到这一行的状态即可,暴力 ( t bitset) 需要 (O(frac{n^4}{w}))

然后我们主动使用分治,考虑中点分治,每次处理过中点的询问。可以处理出 (b(i,j,k)) 表示点 ((i,j)) 是否能到达 ((mid,k)),可以用 ( t bitset) 优化,先分治得到信息之后可以在线询问,那么时间复杂度 (O(frac{n^3log n}{w}+qlog n))

三、总结

我觉得本题重要的是那一步转化,它缩小的答案范围,从而符合了中点分治的模型。

#include <cstdio>
#include <bitset>
using namespace std;
const int M = 505;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,q,D[4*M],num[M];char s[M][M];bitset<M> b[11][M][M];
void cdq(int x,int l,int r,int d)
{
	int mid=(l+r)>>1;D[x]=d;
	for(int i=1;i<=m;i++)
		if(s[mid][i]=='.')
		{
			b[d][mid][i][i]=1;
			b[d][mid][i]|=b[d][mid][i-1];
		}
	for(int i=mid+1;i<=r;i++)
		for(int j=1;j<=m;j++)
			if(s[i][j]=='.')
				b[d][i][j]|=b[d][i-1][j]|b[d][i][j-1];
	for(int i=m;i>=1;i--)
	{
		b[d][mid][i].reset();
		if(s[mid][i]=='.')
		{
			b[d][mid][i][i]=1;
			b[d][mid][i]|=b[d][mid][i+1];
		}
	}
	for(int i=mid-1;i>=l;i--)
		for(int j=m;j>=1;j--)
			if(s[i][j]=='.')
				b[d][i][j]|=b[d][i+1][j]|b[d][i][j+1];
	if(l==r)
	{
		num[l]=x;
		return ;
	}
	cdq(x<<1,l,mid,d+1);cdq(x<<1|1,mid+1,r,d+1);
}
int get(int a,int B,int c,int d)
{
	int x=num[a],y=num[c];
	while(D[x]>D[y]) x>>=1;
	while(D[x]<D[y]) y>>=1;
	while(x!=y) x>>=1,y>>=1;
	return (b[D[x]][a][B]&b[D[x]][c][d]).count();
}
signed main()
{
	n=read();m=read();
	for(int i=1;i<=n;i++)
		scanf("%s",s[i]+1);
	cdq(1,1,n,0);
	q=read();
	while(q--)
	{
		int a=read(),b=read(),c=read(),d=read();
		if(get(a,b,c,d)) puts("Yes");
		else puts("No");
	}
}
原文地址:https://www.cnblogs.com/C202044zxy/p/15106916.html