【日常训练】跑步(BIT)

题目描述

  • 老虎是一名旅游爱好者。

  • 时至 7 7 7 月,正是前往海边度假的好时节。在海边享受阳光,沙滩和海风呼啸,真是虎生的一大享受。海边的居民有着自己的风俗习惯,具体来说,海边可以看做是一个 n × n n \times n n×n 的网格,左上角为 ( 1 , 1 ) (1, 1) (1,1),右下角为 ( n , n ) (n, n) (n,n)。在每个网格中,都生活着渔民一家。对渔民来说,售卖新鲜的贝类显然是谋生的一大工具,而在小镇中,只有网格 ( 1 , 1 ) (1, 1) (1,1) 中有海鲜市场。每天,渔民们会空手从自己的家离开,并通过向左或向上走的方式到达海鲜市场。渔民会在沿途中拾取贝类,并在海鲜市场出售。具体来说,如果一个渔民经过了点 ( i , j ) (i, j) (i,j),那么他能够拾取到价值为 a i , j a_{i, j} ai,j 的贝壳。

  • 显然,每个渔民会选择一条收益最大的路径前进,现在老虎想要知道,所有渔民的最大收益和是多少。

  • 由于自然环境的变化,渔民经过某个位置的代价可能会改变。但自然环境是动态平衡而稳定的,因此渔民经过某个位置时的收益变化不会超过 1 1 1

  • 同时,渔民显然不会做不利于自己的事,因此我们保证条件 a i , j ≥ 0 a_{i, j} \ge 0 ai,j0 始终满足。

  • 对于 100 % 100\% 100% 的数据, 1 ≤ a i , j , n ≤ 2000 1 \le a_{i, j}, n \le 2000 1ai,j,n2000

算法分析

  • 首先我们可以有一个简单的 O ( n 3 ) \mathcal O(n^3) O(n3) 做法:令 f ( i , j ) f(i,j) f(i,j) 表示从 ( i , j ) → ( 1 , 1 ) (i,j)\to (1,1) (i,j)(1,1) 的最大收益,然后就有 f ( i , j ) = max ⁡ { f ( i − 1 , j ) , f ( i , j − 1 ) } + a i , j f(i,j)=\max\{f(i-1,j),f(i,j-1)\}+a_{i,j} f(i,j)=max{f(i1,j),f(i,j1)}+ai,j
  • 每次修改后暴力将 ( x , y ) (x,y) (x,y) ( n , n ) (n,n) (n,n) f f f 修改即可。
  • 考虑每次被修改到的位置满足什么性质。
  • 不难发现:
    • 被修改的位置一定是在 ( x , y ) (x,y) (x,y) ( n , n ) (n,n) (n,n) 之间,并且构成一个连通块。
    • ( i , j − 1 ) 和 ( i − 1 , j ) 都被修改 ⇒ ( i , j ) 被修改 (i,j-1)\text{和}(i-1,j)\text{都被修改}\Rightarrow (i,j)\text{被修改} (i,j1)(i1,j)都被修改(i,j)被修改
  • 不难证明,每一行被修改的恰好是连续的一段,并且这段的 l , r l,r l,r 都是随着行数增加而单调递增的。
  • 因此我们可以得到一个算法,每次修改用两个指针 l , r l,r l,r 表示该行要修改 [ l , r ] [l,r] [l,r] 这个区间。
  • 然后我们从上往下一行行扫过去,在此过程判断 l , r l,r l,r 能否向右移动。
  • 显然对于每次操作,查询次数是 O ( n ) \mathcal O(n) O(n) 的。
  • 然后我们要实现的就是区间加,单点询问,用 BIT \text{BIT} BIT 即可。
  • 时间复杂度 O ( n 2 log ⁡ n ) \mathcal O(n^2\log n) O(n2logn)
#include <bits/stdc++.h>

template <class T>
inline void read(T &x)
{
	static char ch; 
	while (!isdigit(ch = getchar())); 
	x = ch - '0'; 
	while (isdigit(ch = getchar()))
		x = x * 10 + ch - '0'; 
}

inline bool read_opt()
{
	static char ch; 
	while (ch = getchar(), ch != 'U' && ch != 'D'); 
	return ch == 'U'; 
}

typedef long long s64; 

const int MaxN = 2e3 + 5; 

int n; 
int a[MaxN][MaxN], f[MaxN][MaxN]; 

s64 ans; 
s64 c[MaxN][MaxN]; 

inline void add(s64 *c, int x, int del)
{
	for (; x <= n; x += x & -x)
		c[x] += del; 
}

inline s64 query(int i, int x)
{
	s64 res = 0; 
	for (; x; x ^= x & -x)
		res += c[i][x]; 
	return res; 
}

inline void modify(int i, int l, int r, int del)
{
	add(c[i], l, del), add(c[i], r + 1, -del); 
}

int main()
{
	freopen("run.in", "r", stdin); 
	freopen("run.out", "w", stdout); 

	read(n); 
	for (int i = 1; i <= n; ++i)
		for (int j = 1; j <= n; ++j)
			read(a[i][j]); 
	for (int i = 1; i <= n; ++i)
		for (int j = 1; j <= n; ++j)
			ans += (f[i][j] = std::max(f[i - 1][j], f[i][j - 1]) + a[i][j]); 

	printf("%lld\n", ans); 
	for (int i = 1; i <= n; ++i)
		for (int j = 1; j <= n; ++j)
			modify(i, j, j, f[i][j]); 
	for (int orzczk = 1; orzczk <= n; ++orzczk)
	{
		int opt = read_opt(), x, y; 
		read(x), read(y); 
		a[x][y] = opt ? a[x][y] + 1 : a[x][y] - 1; 
		for (int i = x, l = y, r = y; i <= n; ++i)
		{
			while (r < n && query(i, r + 1)
			             != std::max(query(i, r) + (opt ? 1 : -1), query(i - 1, r + 1)) + a[i][r + 1])
				++r; 
			while (l <= r && query(i, l)
						 == std::max(query(i, l - 1), query(i - 1, l)) + a[i][l])
				++l; 
//				printf(":%d %d %d\n", i, l, r); 
			if (l > r) break; 
			ans += (opt ? 1 : -1) * (r - l + 1); 
			modify(i, l, r, opt ? 1 : -1); 
		}

		printf("%lld\n", ans); 
	}

	return 0; 
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值