CF232E Quick Tortoise 题解

考虑离线猫树分治。分治函数 solve(l,r) 表示将所有询问区间 [x1,x2][x_1, x_2] 完全包含于这个区间的询问区间答案处理出来。

考虑在 midmid 两侧的递归处理,现在要考虑的是 x1[l,mid],x2(mid,r]x_1 \in [l,mid], x_2 \in (mid,r] 的答案。

考虑 (x1,y1)(x_1,y_1) 能到 (x2,y2)(x_2,y_2) 的充分必要条件是,存在 x=midx=mid 上的一个点,使得能从 (x1,y1)(x_1,y_1) 到这个点,且可以从这个点到 (x2,y2)(x_2,y_2)

注意到我们可以用两个二维 bitsetf[i][j]g[i][j],分别表示 (i,j)(i,j) 能到的 x=midx=mid 的所有位置。区别是,f 统计的是 i[l,mid]i \in [l,mid](i,j)(i,j)g 统计的是 i(mid,r]i \in (mid,r] 的。这两个都可以通过确定循环顺序做到正确复杂度。

询问答案时,用 & 之后 count 即可。

n,mn, m 同阶,复杂度 O(n3lognω+q(logn+nω))O(\dfrac{n^3 \log n}{\omega} + q(\log n + \dfrac{n}{\omega}))。可以通过。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string>
#include <vector>
#include <queue>
#include <bitset>
#include <stack>
using namespace std;

const int N = 505;
const int Q = 6e5 + 5;

struct Query
{
	int Y1, X2, Y2, id;

	Query() = default;

	Query(int id, int Y1, int X2, int Y2)
		: id(id), Y1(Y1), X2(X2), Y2(Y2)
	{
	}

	bool operator==(const Query& other) const
	{
		return id == other.id && Y1 == other.Y1 && X2 == other.X2 && Y2 == other.Y2;
	}
};

vector<Query> v[N];

int n, m, q;
char c[N][N];
bitset<N> f[N][N], g[N][N];
int cnt[N];
int ans[Q];

int main()
{
	ios::sync_with_stdio(0), cin.tie(0);
	memset(ans, -1, sizeof ans);
	cin >> n >> m;
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= m; j++)
		{
			cin >> c[i][j];
		}
	}
	cin >> q;
	for (int i = 1; i <= q; i++)
	{
		int X1, Y1, X2, Y2;
		cin >> X1 >> Y1 >> X2 >> Y2;
		v[X1].emplace_back(Query(i, Y1, X2, Y2));
		if (X2 >= X1 && Y2 >= Y1) continue;
		ans[i] = 0;
	}
	for (int i = 1; i <= n; i++) sort(v[i].begin(), v[i].end(), [&](const auto& x, const auto& y) {return x.X2 < y.X2; });
	auto solve = [&](auto self, int l, int r)
		{
			if (l == r)
			{
				for (int i = 1; i <= m; i++) cnt[i] = cnt[i - 1] + (c[l][i] == '#');
				auto i1 = upper_bound(v[l].begin(), v[l].end(), Query(0, 0, l - 1, 0), [&](const auto& x, const auto& y) {return x.X2 < y.X2; });
				for (; i1 != v[l].end(); ++i1)
				{
					if ((*i1).X2 != l) return;
					if ((*i1).Y2 < (*i1).Y1) continue;
					int id = (*i1).id;
					ans[id] = !(cnt[(*i1).Y2] - cnt[(*i1).Y1 - 1]);
				}
				return;
			}
			int mid = l + r >> 1;
			self(self, l, mid);
			self(self, mid + 1, r);
			for (int j = m + 1; j >= 0; j--)
			{
				for (int i = mid + 1; i >= l - 1; i--) f[i][j].reset();
			}
			for (int j = m; j >= 1; j--)
			{
				for (int i = mid; i >= l; i--)
				{
					f[i][j].reset();
					if (i == mid)
					{
						if (c[i][j] == '.')
						{
							f[i][j][j] = 1;
							f[i][j] |= f[i][j + 1];
						}
						continue;
					}
					if (c[i][j] == '.')
					{
						f[i][j] = f[i][j + 1] | f[i + 1][j];
					}
				}
			}
			for (int i = mid; i <= r + 1; i++)
			{
				for (int j = 0; j <= m + 1; j++) g[i][j].reset();
			}
			for (int j = 1; j <= m; j++)
			{
				for (int i = mid + 1; i <= r; i++)
				{
					if (i == mid + 1)
					{
						if (c[i][j] == '.')
						{
							if (c[mid][j] == '.') g[i][j][j] = 1;
							g[i][j] |= g[i][j - 1];
						}
						continue;
					}
					if (c[i][j] == '.')
					{
						g[i][j] = g[i - 1][j] | g[i][j - 1];
					}
				}
			}
			for (int i = l; i <= mid; i++)
			{
				// (mid,r]
				auto it = upper_bound(v[i].begin(), v[i].end(), Query(0, 0, mid, 0), [&](const auto& x, const auto& y) {return x.X2 < y.X2; });
				for (; it != v[i].end(); ++it)
				{
					Query p = *it;
					if (p.X2 <= mid || p.X2 > r) break;
					if (~ans[p.id]) continue;
					//cout << "!!!!!: " << l << " " << r << " " << ((g[p.X2][p.Y2]).count()) << "\n";
					ans[p.id] = (int)((bool)((f[i][p.Y1] & g[p.X2][p.Y2]).count()));
				}
			}
		};
	solve(solve, 1, n);
	for (int i = 1; i <= q; i++)
	{
		cout << (ans[i] ? "Yes\n" : "No\n");
	}
	return 0;
}
posted @   HappyBobb  阅读(4)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示