【MST+倍增】BZOJ4793 [CERC2016] Hangar Hurdles

【题目】
BZOJ
给定一个 n × n n\times n n×n的矩阵,矩阵中有空地和障碍两种格子。设集装箱的坐标为其中点坐标,其长度均为奇数, Q Q Q次询问能将最多大的集装箱不触碰障碍的情况下从起点运到终点。
n ≤ 1000. Q ≤ 3 × 1 0 5 n\leq 1000.Q\leq 3\times 10^5 n1000.Q3×105

【解题思路】
首先我们显然要求出以每个点为中心的集装箱最大能是多少。这个问题可以通过预处理出一个点向上下左右能扩展到多少,然后以每一行单位处理出区间往上下最多能扩展多少的 RMQ \text{RMQ} RMQ数组,这样每个点可以通过二分答案来做到总 O ( n 2 log ⁡ n ) O(n^2\log n) O(n2logn)。当然这个问题可以利用每次往右移动一格,扩展答案至多减少一的性质做到 O ( n 2 ) O(n^2) O(n2)。(不过我比较懒就没怎么想)

接下来实际上就是要求两点间任意路径,使得最小值最大。这是一个经典问题,我们可以处理出关于点的最大生成树,接着答案就是路径上最小值。具体来说,我们从大到小枚举每一个点,将其标记为可用,若一个点被标记为可用时,与它相邻的点是可用的且它们不在一个连通块,就将其连起来。处理路径最小值可以使用简单倍增,注意一些细节即可。

复杂度 O ( ( n 2 + Q ) log ⁡ ( n 2 ) ) O((n^2+Q)\log (n^2)) O((n2+Q)log(n2))

【参考代码】

#include<bits/stdc++.h>
#define mkp make_pair
#define fi first
#define se second
#define pb push_back
using namespace std;

typedef pair<int,int> pii;
const int N=1003,M=N*N,inf=0x3f3f3f3f;
const int dx[]={0,1,0,-1},dy[]={1,0,-1,0};
int n,Q,fc[25],Log[M];

namespace IO
{
	int read()
	{
		int ret=0;char c=getchar();
		while(!isdigit(c)) c=getchar();
		while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
		return ret;
	}
	void write(int x){if(x>9)write(x/10);putchar(x%10^48);}
	void writeln(int x){write(x);putchar('\n');}
}
using namespace IO;

namespace Tree
{
	int cnt,tot,head[M],bl[M],len[M],dep[M],f[M];
	int fa[22][M],mi[22][M];
	struct Tway{int v,nex;}e[M<<1];
	void add(int u,int v)
	{
		e[++tot]=(Tway){v,head[u]};head[u]=tot;
		e[++tot]=(Tway){u,head[v]};head[v]=tot;
	}
	int id(int x,int y){return (x-1)*(n+1)+y;}
	//void re(int x){printf("%d %d\n",x/(n+1)+1,x%(n+1));}
	void dfs(int x)
	{
		bl[x]=cnt;mi[0][x]=len[x];
		for(int j=1;fc[j]<=dep[x];++j) 
			fa[j][x]=fa[j-1][fa[j-1][x]],mi[j][x]=min(mi[j-1][x],mi[j-1][fa[j-1][x]]);
		for(int i=head[x];i;i=e[i].nex)
		{
			int v=e[i].v;
			if(v==fa[0][x]) continue;
			fa[0][v]=x;dep[v]=dep[x]+1;dfs(v);
		}
	}
	int lca(int x,int y)
	{
		if(dep[x]<dep[y]) swap(x,y);
		for(int i=0,t=dep[x]-dep[y];i<21;++i) 
			if(t&fc[i]) x=fa[i][x];
		for(int i=20;~i;--i) if(fa[i][x]^fa[i][y])
			x=fa[i][x],y=fa[i][y];
		return x==y?x:fa[0][x];
	}
	int jump(int x,int d)//calc except lca
	{
		//printf("jump:");re(x);
		int res=inf;
		for(int i=0;i<21;++i) if(d&fc[i])
			res=min(res,mi[i][x]),x=fa[i][x];
		//puts("jumpend");
		return res;
	}
	int calc(int x,int y)
	{
		if(bl[x]^bl[y]) return 0;
		//printf("%d %d %d %d\n",x/(n+1)+1,x%(n+1),y/(n+1)+1,y%(n+1));
		int z=lca(x,y),res=mi[0][z];
		//printf("lca:");re(z);
		res=min(res,jump(x,dep[x]-dep[z]));
		res=min(res,jump(y,dep[y]-dep[z]));
		return res;
	}
	int findf(int x){return f[x]==x?x:f[x]=findf(f[x]);}
}
using namespace Tree;

namespace DreamLolita
{
	int cb;
	int a[N][N],L[N][N],R[N][N],U[N][N],D[N][N],st[13][N];
	bool cuse[N][N];
	char s[N];

	struct data
	{
		int l,d;
		data(int _l=0,int _d=0):l(_l),d(_d){}
		bool operator < (const data&rhs)const{return l>rhs.l;}
	}b[M];
	void init()
	{
		n=read();
		for(int i=1;i<=n;++i) 
		{
			scanf("%s",s+1);
			for(int j=1;j<=n;++j) if(s[j]=='.') a[i][j]=1;
		}
		for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) if(a[i][j]) 
		{
			if(a[i-1][j]) U[i][j]=U[i-1][j]+1;
			else U[i][j]=1;
			if(a[i][j-1]) L[i][j]=L[i][j-1]+1;
			else L[i][j]=1;
		}
		for(int i=n;i;--i) for(int j=n;j;--j) if(a[i][j])
		{
			if(a[i+1][j]) D[i][j]=D[i+1][j]+1;
			else D[i][j]=1;
			if(a[i][j+1]) R[i][j]=R[i][j+1]+1;
			else R[i][j]=1;
		}
		fc[0]=1;for(int i=1;i<23;++i)fc[i]=fc[i-1]<<1;
		for(int i=2;i<M;++i) Log[i]=Log[i>>1]+1;
	}
	void getrmq(int x)
	{
		for(int i=1;i<=n;++i) st[0][i]=min(U[x][i],D[x][i]);
		for(int j=1;j<11;++j) for(int i=1;i+fc[j]-1<=n;++i)
			st[j][i]=min(st[j-1][i],st[j-1][i+fc[j-1]]); 
	}
	int query(int l,int r)
	{
		if(l>r) return 0;
		int t=Log[r-l+1];
		return min(st[t][l],st[t][r-fc[t]+1]);
	}
	void getlen()
	{
		for(int i=1;i<=n;++i)
		{
			getrmq(i);
			for(int j=1;j<=n;++j) if(a[i][j])
			{
				int l=1,r=min(L[i][j],R[i][j]),res=1;
				while(l<=r)
				{
					int mid=(l+r)>>1;
					if(query(j-mid+1,j+mid-1)>=mid) res=mid,l=mid+1;
					else r=mid-1; 
				}
				len[id(i,j)]=res*2-1;
			}
		}
		//for(int i=1;i<=n;++i,puts("")) for(int j=1;j<=n;++j) printf("%d ",len[id(i,j)]); puts("");

	}
	void inittree()
	{
		for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) f[id(i,j)]=id(i,j);
		for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) if(a[i][j])
			b[++cb]=data(len[id(i,j)],id(i,j));
		sort(b+1,b+cb+1);
		for(int i=1;i<=cb;++i) 
		{
			int y=b[i].d%(n+1),x=b[i].d/(n+1)+1,now=id(x,y);
			cuse[x][y]=1;
			for(int d=0;d<4;++d)
			{
				int nx=x+dx[d],ny=y+dy[d],nid=id(nx,ny);
				if(cuse[nx][ny] && findf(now)!=findf(nid))
				{
					//printf("add:%d %d %d %d\n",x,y,nx,ny);
					add(now,nid);
					f[findf(now)]=f[findf(nid)];
				} 
			}
		}
		for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) 
			if(a[i][j] && !bl[id(i,j)]) ++cnt,dfs(id(i,j));
		//for(int i=1;i<=n;++i,puts("")) for(int j=1;j<=n;++j) printf("%d ",bl[id(i,j)]); puts("");
	}
	void getans()
	{
		Q=read();
		while(Q--)
		{
			int xl=read(),yl=read(),xr=read(),yr=read();
			writeln(calc(id(xl,yl),id(xr,yr)));
		}
	}
	void solution()
	{
		init();getlen();
		inittree();getans();
	}
}

int main()
{
	DreamLolita::solution();
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值