Jzoj P6224 正方形___动态规划+并查集

127 篇文章 0 订阅
71 篇文章 0 订阅

题目大意:

在这里插入图片描述
在这里插入图片描述

分析:

考虑离线:
f i , j f_{i,j} fi,j表示 点 ( i , j ) 点(i,j) (i,j)为右下角最多能向左上拓展多长的正方形,其对角线长度即为 f i , j f_{i,j} fi,j
任意相邻两点 u , v u,v u,v间建边 ( u , v , w ) (u,v,w) (u,v,w) w = m i n ( f u , f v ) w=min(f_u,f_v) w=min(fu,fv),然后将这些边存起来按 w w w降序排列
将询问 ( x 1 , y 1 , x 2 , y 2 , w ) (x_1,y_1,x_2,y_2,w) (x1,y1,x2,y2,w)存起来按 w w w降序排列
枚举询问,当前是第 i i i w w w对应的询问,
> = w i >=w_i >=wi的所有 ( u , v , w ) (u,v,w) (u,v,w)解封,然后判断 ( x 1 , y 1 ) (x_1,y_1) (x1,y1) ( x 2 , y 2 ) (x_2,y_2) (x2,y2)是否连通,
判断是否连通可以用并查集,每解封一条边就用并查集将边的两个点的集合合并

PS:注意考虑 f x 1 , y 1 &lt; w , f x 2 , y 2 &lt; w f_{x_1,y_1}&lt;w,f_{x_2,y_2}&lt;w fx1,y1<w,fx2,y2<w的情况

代码:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <queue>
#include <cstring>
#include <algorithm>

#define rep(i, st, ed) for (int i = st; i <= ed; i++)
#define rwp(i, ed, st) for (int i = ed; i >= st; i--)

#define M 100005
#define N 1005

using namespace std;

struct Node { int u, v, w; }e[2*N*N]; 
struct Aske { int id, ax, ay, bx, by, lim; }c[M];

int id[N][N], f[N][N], a[N][N], fa[N*N], ans[M], n, m, q, tot, cnt;
char s[N];

bool cmp1(Node aa, Node bb) { return aa.w > bb.w; }

bool cmp2(Aske aa, Aske bb) { return aa.lim > bb.lim; }

int find(int x) { return (fa[x] == x ? x : (fa[x] = find(fa[x]))); }

void merge(int id)
{
	int pos1 = find(e[id].u);
	int pos2 = find(e[id].v);
	if (pos1 != pos2) fa[pos1] = pos2;
}

int main()
{
	freopen("square.in", "r", stdin);
	freopen("square.out", "w", stdout);
	scanf("%d %d %d", &n, &m, &q);
	rep(i, 1, n)
	{
		scanf("%s", s + 1); 
		rep(j, 1, m) a[i][j] = s[j] - '0', id[i][j] = ++tot, fa[id[i][j]] = tot;
	}
	rep(i, 1, n)
	    rep(j, 1, m) 
		    if (!a[i][j]) f[i][j] = min(f[i - 1][j - 1], min(f[i][j - 1], f[i - 1][j])) + 1;
	rep(i, 1, n) 
	   rep(j, 1, m - 1) e[++cnt].u = id[i][j], e[cnt].v = id[i][j + 1], e[cnt].w = min(f[i][j], f[i][j + 1]);
	rep(i, 1, m)
       rep(j, 1, n - 1) e[++cnt].u = id[j][i], e[cnt].v = id[j + 1][i], e[cnt].w = min(f[j][i], f[j + 1][i]);
    
	sort(e + 1, e + cnt + 1, cmp1);
	
	rep(i, 1, q) scanf("%d %d %d %d %d", &c[i].ax, &c[i].ay, &c[i].bx, &c[i].by, &c[i].lim), c[i].id = i;
	sort(c + 1, c + q + 1, cmp2);
	
	int now = 1, pos1, pos2;
	
	rep(i, 1, q)
	{
		if (f[c[i].ax][c[i].ay] < c[i].lim || f[c[i].bx][c[i].by] < c[i].lim) { ans[c[i].id] = 0; continue; }  
		while (now <= cnt && e[now].w >= c[i].lim) { merge(now); now++; }
	    pos1 = id[c[i].ax][c[i].ay]; pos1 = find(pos1);
	    pos2 = id[c[i].bx][c[i].by]; pos2 = find(pos2);
	    if (pos1 == pos2) ans[c[i].id] = 1; else ans[c[i].id] = 0;
	}
	rep(i, 1, q) printf("%s\n", (ans[i] ? "Yes" : "No"));
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值