首页 > 其他分享 >P2254 [NOI2005] 瑰丽华尔兹 题解

P2254 [NOI2005] 瑰丽华尔兹 题解

时间:2022-11-11 14:22:53浏览次数:61  
标签:ch int 题解 P2254 back NOI2005 && front empty

注意到可以设朴素转移方程 \(f_{t,i,j}\) 表示 \(t\) 时刻钢琴在 \((i,j)\) 时的最长滑动距离,这样复杂度是 \(O(nmt)\) 的,过不去。不过听说加点奇怪的优化能过?

考虑一段时刻内钢琴的移动方向是连续的,于是可以改一改状态,\(f_{p,i,j}\) 表示第 \(p\) 段时间结束后钢琴在 \((i,j)\) 时的最长滑动距离。接下来以向右滑动为例子。

注意到可以写出转移方程:

\[f_{p,i,j}=\max\{f_{p-1,i,k}+j-k\mid k\le j\} \]

然后这是一个单调队列优化的形式,这样总复杂度就是 \(O(nmk)\) 的,四个方向都这样搞一下就好了。

Code:

/*
========= Plozia =========
	Author:Plozia
	Problem:P2254 [NOI2005] 瑰丽华尔兹
	Date:2022/11/11
========= Plozia =========
*/

#include <bits/stdc++.h>
typedef long long LL;
using std::deque;

const int MAXN = 200 + 5, INF = 0x3f3f3f3f;
int n, m, Start, End, k, f[2][MAXN][MAXN];
char ch[MAXN][MAXN];
struct node { int l, r, d; bool operator <(const node &fir)const { return l < fir.l; } } a[MAXN];

int Read()
{
	int sum = 0, fh = 1; char ch = getchar();
	for (; ch < '0' || ch > '9'; ch = getchar()) fh -= (ch == '-') << 1;
	for (; ch >= '0' && ch <= '9'; ch = getchar()) sum = (sum << 3) + (sum << 1) + (ch ^ 48);
	return sum * fh;
}

int main()
{
	n = Read(), m = Read(), Start = Read(), End = Read(), k = Read();
	for (int i = 1; i <= n; ++i) for (int j = 1; j <= m; ++j) std::cin >> ch[i][j];
	for (int i = 1; i <= k; ++i) a[i].l = Read(), a[i].r = Read(), a[i].d = Read();
	int now = 0; for (int i = 1; i <= n; ++i) for (int j = 1; j <= m; ++j) f[now][i][j] = -INF;
	std::sort(a + 1, a + k + 1); f[now][Start][End] = 0;
	for (int _ = 1; _ <= k; ++_)
	{
		now ^= 1; int len = a[_].r - a[_].l + 1;
		for (int i = 1; i <= n; ++i) for (int j = 1; j <= m; ++j) f[now][i][j] = -INF;
		if (a[_].d == 1) // f[i][j] = f[k][j] + k - i
		{
			for (int j = 1; j <= m; ++j)
			{
				deque <int> q;
				for (int i = n; i >= 1; --i)
				{
					while (!q.empty() && q.front() - i > len) q.pop_front();
					if (ch[i][j] == 'x') q.clear();
					else
					{
						while (!q.empty() && f[now ^ 1][q.back()][j] + q.back() <= f[now ^ 1][i][j] + i) q.pop_back();
						q.push_back(i); f[now][i][j] = f[now ^ 1][q.front()][j] + q.front() - i;
					}
				}
			}
		}
		else if (a[_].d == 2) // f[i][j] = f[k][j] + i - k
		{
			for (int j = 1; j <= m; ++j)
			{
				deque <int> q;
				for (int i = 1; i <= n; ++i)
				{
					while (!q.empty() && i - q.front() > len) q.pop_front();
					if (ch[i][j] == 'x') q.clear();
					else
					{
						while (!q.empty() && f[now ^ 1][q.back()][j] - q.back() <= f[now ^ 1][i][j] - i) q.pop_back();
						q.push_back(i); f[now][i][j] = f[now ^ 1][q.front()][j] + i - q.front();
					}
				}
			}
		}
		else if (a[_].d == 3) // f[i][j] = f[i][k] + k - j
		{
			for (int i = 1; i <= n; ++i)
			{
				deque <int> q;
				for (int j = m; j >= 1; --j)
				{
					while (!q.empty() && q.front() - j > len) q.pop_front();
					if (ch[i][j] == 'x') q.clear();
					else
					{
						while (!q.empty() && f[now ^ 1][i][q.back()] + q.back() <= f[now ^ 1][i][j] + j) q.pop_back();
						q.push_back(j); f[now][i][j] = f[now ^ 1][i][q.front()] + q.front() - j;
					}
				}
			}
		}
		else // f[i][j] = f[i][k] + j - k
		{
			for (int i = 1; i <= n; ++i)
			{
				deque <int> q;
				for (int j = 1; j <= m; ++j)
				{
					while (!q.empty() && j - q.front() > len) q.pop_front();
					if (ch[i][j] == 'x') q.clear();
					else
					{
						while (!q.empty() && f[now ^ 1][i][q.back()] - q.back() <= f[now ^ 1][i][j] - j) q.pop_back();
						q.push_back(j); f[now][i][j] = f[now ^ 1][i][q.front()] + j - q.front();
					}
				}
			}
		}
	}
	int ans = -INF;
	for (int i = 1; i <= n; ++i) for (int j = 1; j <= m; ++j) ans = std::max(ans, f[now][i][j]);
	printf("%d\n", ans); return 0;
}

标签:ch,int,题解,P2254,back,NOI2005,&&,front,empty
From: https://www.cnblogs.com/Plozia/p/16880325.html

相关文章

  • MySQL慢查询(下):问题解决,应用总结
    上篇回顾继上两篇:​​MySQL慢查询(上):你知道为啥会慢么?​​​​MySQL慢查询(中):正确的处理姿势,你get到了吗?​​在以上两篇内容中,我们一起探索了这些内容:SQL执行过程查询SQL为什......
  • 「题解」Codeforces 1098D Eels
    暴力是,每次挑出最小的两个合并。需要观察到没有产生贡献的次数很小。考虑最小的那个数的大小,如果一次合并没有产生贡献,那么最小的数至少\(\times2\).所以最多会有\(\mat......
  • [题解] [CSP-J 2022] 逻辑表达式 思路整理
    标签:分治。题目传送门:P8815[CSP-J2022]逻辑表达式题目大意给一个包含0、1、|、&、(、)的逻辑表达式(保证正确)。在计算表达式时采用“短路”策略:计算表达式a&b......
  • vs 加入目录下的文件不在解决方案窗口显示(我的是unreal,其他的也成立),必须手动加的问
    1、网上说的显示全部然后把非活动的包含,我的可能是项目太大,不行;2、使用一个foldertosolutionfolder插件,也不行,这个会将子文件夹单独生成一个项目;最后方案:删除*.sln文件,......
  • 题解 P4778【Counting swaps】
    problem一次操作指随意选定\(x,y\)并交换\(a_x,a_y\),请问有多少种方案,能用最少的操作次数重排一个排列\(a\)?\(n\leq10^5,P=10^9+7\)。solution0连边\(i\toa_i\)......
  • 模拟与高精度题解
    此题目特征为储存数字超过longlong类型,c++无法用一个变量存储全部数字解法为开数组来储存各个位上的数字1.字符高精度直接以两种方式处理字符即可#include<bits/std......
  • 小姿势 之 Android Studio 3.5 Retry 问题解决
    总会有那么一个人,让你觉得这个世界一切都是值得的。纵使结果不尽人意,曾经拥有即是最好。前言家里的MBP静静地躺了一段时间,某天心血来潮想嗨起来,其实就是瞎折腾一把,结果......
  • 洛谷 P4423 [BJWC2011]最小三角形 题解
    求平面最近点对的时候有这样一种思路:将所有点全部绕原点旋转同一个角度,然后按横坐标排序。根据数学直觉,在随机旋转后,答案中的两个点在数组中肯定不会离得太远。把这种......
  • P4407 [JSOI2009] 电子字典 题解
    题目:P4407这题差不多就是P1688的改版。参考一下我在P1688的做法,我们继续使用Hash,然后只要考虑如何去重就好了。于是就有了这个暴力的想法:#代表修改,@代表添加,$代......
  • [AGC040F] Two Pieces 题解
    linkSolution这个题真的挺难的。/kk看了一个下午的题解才搞懂。/fn我们发现我们如果设状态\((x,d)\)表示前面的一个点在\(x\),另一个在\(x-d\),那么三种操作相当于:......