JZOJ5890.【NOIP2018模拟9.29】方格问题

15 篇文章 0 订阅
5 篇文章 0 订阅

题意:

数据范围:


时限6S

Analysis:

假设我们已知哪些格子不能走,且仅有第二个操作怎么做?可以用类似 Z J O I 2016 ZJOI2016 ZJOI2016旅行者的分治最短路的方式去做。那么这题我们格子是会发生变化的,需要实时知道信息。但我们可以知道每个格子变为不能走的时间,那么定义这个时间为这个格子的权值,一条路径的权值为这条路径上格子权值最小值。显然对于一个询问 ( x , y ) , ( x 1 , y 1 ) (x,y),(x1,y1) (x,y),(x1,y1),我们就需要找一条权值最大的路径,看这个权值是否大于该询问的时间。对于询问和修改离线下来,先整体二分出每个格子变不能走的时间,然后用类似 Z J O I 2016 ZJOI2016 ZJOI2016旅行者的分治 D P DP DP去解决询问就好了。
复杂度: O ( ( n m + q ) log ⁡ 2 2 n m + q n m log ⁡ 2 n m ) O((nm+q)\log_2^2{nm} + q\sqrt{nm}\log_2{nm}) O((nm+q)log22nm+qnm log2nm)

Code:

# include<cstdio>
# include<cstring>
# include<algorithm>
using namespace std;
# define id(x,y) ((x) - 1) * m + (y)
const int N = 2e5 + 5;
typedef long long ll;
struct node
{
	int x,l,r,v,id;
}q[N << 1],a[N << 1],b[N << 1],z[N];
ll t[N];
int ti[N],ans[N],f[N],lim[N],g[N];
int n,m,L,Q,h,h1;
inline int read()
{
	int x = 0; char ch = getchar();
	for (; ch < '0' || ch > '9' ; ch = getchar());
	for (; ch >= '0' && ch <= '9' ; ch = getchar()) x = x * 10 + ch - '0';
	return x;
}
bool cmp(node a,node b) { return a.x == b.x ? a.id > b.id : a.x < b.x; }
inline void add(int x,int w)
{ for (int i = x ; i <= m ; i += i & (-i)) t[i] += w; }
inline ll qry(int x)
{
	ll ret = 0;
	for (int i = x ; i ; i -= i & (-i)) ret += t[i];
	return ret;
}
inline void divi(int l,int r,int ql,int qr)
{
	if (ql > qr) return;
	if (l == r)
	{
		for (int i = ql ; i <= qr ; ++i)
		if (!q[i].id) ti[id(q[i].x,q[i].l)] = l;
		return;
	}
	int mid = (l + r) >> 1,h = 0,h1 = 0;
	sort(q + ql,q + qr + 1,cmp);
	for (int i = ql ; i <= qr ; ++i)
	{
		if (q[i].id)
		{
			if (q[i].id <= mid) add(q[i].l,q[i].v),add(q[i].r + 1,-q[i].v),a[++h] = q[i];
			else b[++h1] = q[i];
		}else
		{
			if (qry(q[i].l) + q[i].v >= L) a[++h] = q[i];
			else q[i].v += qry(q[i].l),b[++h1] = q[i];
		}
	}
	for (int i = ql ; i <= ql + h - 1 ; ++i) q[i] = a[i - ql + 1];
	for (int i = qr ; i >= qr - h1 + 1 ; --i) q[i] = b[qr - i + 1];
	divi(l,mid,ql,ql + h - 1),divi(mid + 1,r,qr - h1 + 1,qr);
}
inline void calc(int x,int y,int x1,int y1,int opt)
{
	if (opt == 1)
	{
		for (int i = x1 ; i >= x ; --i)
			for (int j = y1 ; j >= y ; --j)
			{
				f[id(i,j)] = f[id(i + 1,j)];
				if (j + 1 <= y1) f[id(i,j)] = max(f[id(i,j)],f[id(i,j + 1)]);
				f[id(i,j)] = min(ti[id(i,j)],f[id(i,j)]);
			}
	}else if (opt == 2)
	{
		for (int i = x ; i <= x1 ; ++i)
			for (int j = y ; j <= y1 ; ++j)
			{
				f[id(i,j)] = f[id(i - 1,j)];
				if (j - 1 >= y) f[id(i,j)] = max(f[id(i,j)],f[id(i,j - 1)]);
				f[id(i,j)] = min(ti[id(i,j)],f[id(i,j)]);
			}
	}else if (opt == 3)
	{
		for (int j = y1 ; j >= y ; --j)
			for (int i = x1 ; i >= x ; --i)
			{
				f[id(i,j)] = f[id(i,j + 1)];
				if (i + 1 <= x1) f[id(i,j)] = max(f[id(i,j)],f[id(i + 1,j)]);
				f[id(i,j)] = min(ti[id(i,j)],f[id(i,j)]);
			}
	}else if (opt == 4)
	{
		for (int j = y ; j <= y1 ; ++j)
			for (int i = x ; i <= x1 ; ++i)
			{
				f[id(i,j)] = f[id(i,j - 1)];
				if (i - 1 >= x) f[id(i,j)] = max(f[id(i,j)],f[id(i - 1,j)]);
				f[id(i,j)] = min(ti[id(i,j)],f[id(i,j)]);
			}
	}
}
inline void solve(int x,int y,int x1,int y1,int l,int r)
{
	if (l > r || x > x1 || y > y1) return;
	if (x1 - x > y1 - y)
	{
		int mid = (x + x1) >> 1,h = 0,h1 = 0;
		for (int i = l ; i <= r ; ++i)
		if (z[i].x < mid && z[i].r < mid) a[++h] = z[i];
		else if (z[i].x > mid && z[i].r > mid) b[++h1] = z[i];
		for (int i = y ; i <= y1 ; ++i)
		{
			f[id(mid,i)] = ti[id(mid,i)];
			for (int j = i - 1 ; j >= y ; --j) f[id(mid,j)] = min(ti[id(mid,j)],f[id(mid,j + 1)]);
			for (int j = i + 1 ; j <= y1 ; ++j) f[id(mid,j)] = 0;
			calc(x,y,mid - 1,y1,1);
			for (int j = i - 1 ; j >= y ; --j) g[j] = f[id(mid,j)],f[id(mid,j)] = 0;
			for (int j = i + 1 ; j <= y1 ; ++j) f[id(mid,j)] = min(ti[id(mid,j)],f[id(mid,j - 1)]);
			calc(mid + 1,y,x1,y1,2);
			for (int j = i - 1 ; j >= y ; --j) f[id(mid,j)] = g[j];
			for (int j = l ; j <= r ; ++j)
			if (z[j].x <= mid && z[j].r >= mid) ans[z[j].id] = max(ans[z[j].id],min(f[id(z[j].x,z[j].l)],f[id(z[j].r,z[j].v)]));
		}
		for (int i = l ; i <= l + h - 1 ; ++i) z[i] = a[i - l + 1];
		for (int i = r ; i >= r - h1 + 1 ; --i) z[i] = b[r - i + 1];
		solve(x,y,mid - 1,y1,l,l + h - 1);
		solve(mid + 1,y,x1,y1,r - h1 + 1,r);
	}else
	{
		int mid = (y + y1) >> 1,h = 0,h1 = 0;
		for (int i = l ; i <= r ; ++i)
		if (z[i].l < mid && z[i].v < mid) a[++h] = z[i];
		else if (z[i].l > mid && z[i].v > mid) b[++h1] = z[i];
		for (int i = x ; i <= x1 ; ++i)
		{
			f[id(i,mid)] = ti[id(i,mid)];
			for (int j = i - 1 ; j >= x ; --j) f[id(j,mid)] = min(ti[id(j,mid)],f[id(j + 1,mid)]);
			for (int j = i + 1 ; j <= x1 ; ++j) f[id(j,mid)] = 0;
			calc(x,y,x1,mid - 1,3);
			for (int j = i - 1 ; j >= x ; --j) g[j] = f[id(j,mid)],f[id(j,mid)] = 0;
			for (int j = i + 1 ; j <= x1 ; ++j) f[id(j,mid)] = min(ti[id(j,mid)],f[id(j - 1,mid)]);
			calc(x,mid + 1,x1,y1,4);
			for (int j = i - 1 ; j >= x ; --j) f[id(j,mid)] = g[j];
			for (int j = l ; j <= r ; ++j)
			if (z[j].l <= mid && z[j].v >= mid) ans[z[j].id] = max(ans[z[j].id],min(f[id(z[j].x,z[j].l)],f[id(z[j].r,z[j].v)]));
		}
		for (int i = l ; i <= l + h - 1 ; ++i) z[i] = a[i - l + 1];
		for (int i = r ; i >= r - h1 + 1 ; --i) z[i] = b[r - i + 1];
		solve(x,y,x1,mid - 1,l,l + h - 1);
		solve(x,mid + 1,x1,y1,r - h1 + 1,r);
	}
}
int main()
{
	freopen("grid.in","r",stdin);
	freopen("grid.out","w",stdout);
	n = read(),m = read(),L = read(),Q = read();
	for (int i = 1 ; i <= Q ; ++i)
	{
		int opt = read();
		if (opt == 1)
		{
			q[++h].x = read(),q[h].l = q[h + 1].l = read();
			q[++h].x = read() + 1,q[h].r = q[h - 1].r = read();
			q[h - 1].v = read(),q[h].v = -q[h - 1].v,q[h].id = q[h - 1].id = i;
		}else z[++h1].x = read(),z[h1].l = read(),z[h1].r = read(),z[h1].v = read(),z[h1].id = h1,lim[h1] = i;
	}
	for (int i = 1 ; i <= n ; ++i)
		for (int j = 1 ; j <= m ; ++j) q[++h] = (node){i,j,0,0,0};
	divi(1,Q,1,h);
	solve(1,1,n,m,1,h1);
	for (int i = 1 ; i <= h1 ; ++i)
	if (ans[i] < lim[i]) puts("no");
	else puts("yes");
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值