题目传送门
分析:
暴力DP无法通过,考虑按横坐标进行分治
假设目前分治区间中点为(mid),沿这一列将矩形划分成两份,如果一次询问两端分别在两边,那么其路线一定经过(mid)这一列上的某一点
设(f[x][y][k]=0/1)表示((x,y))能否走到((k,mid))
最后答案只需要判断是否存在一个(k)使得(f[x1][y1][k]&f[x2][y2][k]=1)
暴力转移是(O(nm^2logn))的,会超时
发现(f)值只有(0/1),尝试用bitset优化这个转移
复杂度变为(O(frac{nm^2logn}{32})),CF少爷机可以通过
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<algorithm>
#include<queue>
#include<bitset>
#include<map>
#include<set>
#define maxn 505
#define maxm 1000005
#define INF (1ll<<60)
using namespace std;
inline int getint()
{
int num=0,flag=1;char c;
while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
while(c>='0'&&c<='9')num=num*10+c-48,c=getchar();
return num*flag;
}
int n,m,Q;
bitset<maxn>f[maxn][maxn],g[maxn][maxn];
char s[maxn][maxn];
bool ans[maxm];
struct node{
int x1,y1,x2,y2,id;
}q[maxm],tmp1[maxm],tmp2[maxm];
inline void solve(int l,int r,int ql,int qr)
{
if(l>r||ql>qr)return;
int cnt1=0,cnt2=0,mid=(l+r)>>1;
for(int i=1;i<=m;i++)f[mid][i]=g[mid][i]=0;
for(int i=m;i;i--)if(s[mid][i]=='.')f[mid][i][i]=1,f[mid][i]|=f[mid][i+1];
for(int i=1;i<=m;i++)if(s[mid][i]=='.')g[mid][i][i]=1,g[mid][i]|=g[mid][i-1];
for(int i=mid-1;i>=l;i--)for(int j=m;j;j--)
if(s[i][j]=='.')f[i][j]=f[i+1][j]|f[i][j+1];
else f[i][j]=0;
for(int i=mid+1;i<=r;i++)for(int j=1;j<=m;j++)
if(s[i][j]=='.')g[i][j]=g[i-1][j]|g[i][j-1];
else g[i][j]=0;
for(int i=ql;i<=qr;i++)
{
if(q[i].x2<mid){tmp1[++cnt1]=q[i];continue;}
if(q[i].x1>mid){tmp2[++cnt2]=q[i];continue;}
ans[q[i].id]=(f[q[i].x1][q[i].y1]&g[q[i].x2][q[i].y2]).any();
}
for(int i=1;i<=cnt1;i++)q[ql+i-1]=tmp1[i];
for(int i=1;i<=cnt2;i++)q[qr-i+1]=tmp2[i];
solve(l,mid-1,ql,ql+cnt1-1);
solve(mid+1,r,qr-cnt2+1,qr);
}
int main()
{
n=getint(),m=getint();
for(int i=1;i<=n;i++)scanf("%s",s[i]+1);
Q=getint();
for(int i=1;i<=Q;i++)q[i].x1=getint(),q[i].y1=getint(),q[i].x2=getint(),q[i].y2=getint(),q[i].id=i;
solve(1,n,1,Q);
for(int i=1;i<=Q;i++)puts(ans[i]?"Yes":"No");
}