P6370 [COCI2006-2007#6] KAMEN 题解

发布时间 2023-12-19 12:09:38作者: Creeper_l

原题链接:P6370

思路

题意不多赘述。

首先这道题的 \(60\) 分暴力很好打,直接按题目中的操作做即可,时间复杂度 \(O(nr)\)

考虑优化暴力。我们会发现很多次石头的起始点为同一列的情况,其实每一次下落的轨迹是差不多的。具体来讲应该是第一次下落的轨迹一定包含了后面每一次的轨迹。所以我们可以模拟一个类似于栈的东西 \(s_{i,j}\) 表示起始点在第 \(i\) 列,当前在第 \(j\) 行(即跳过的步数为 \(j\),因为每跳一次一定下落一行)时,当前在 \(s_{i,j}\) 列。那么,对于起始点在每一列的情况,第一次直接暴力将栈填满,之后的每一次再看栈顶的位置合不合法,如果不合法的话就弹出直到合法为止。则栈顶的位置就是这颗石头所在的位置。

因为起始点只可能有 \(O(c)\) 个,且对于每个起始点的暴力操作最多 \(O(rc)\) 次,于是总时间复杂度 \(O(rc^{2}\))。

Code

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define inf 0x3f
#define inf_db 127
#define ls id << 1
#define rs id << 1 | 1
#define re register
#define endl '\n'
typedef pair <int,int> pii;
const int MAXN = 3e4 + 10;
const int MAXM = 30 + 10;
int n,m,q,x,s[MAXM][MAXN],cnt[MAXN];
char c[MAXN][MAXM];
signed main()
{
	scanf("%lld%lld",&n,&m);
	for(int i = 1;i <= n;i = -~i)  scanf("%s",(c[i] + 1)); 
	for(int i = 1;i <= m;i = -~i) c[n + 1][i] = 'X';
	for(int i = 1;i <= m;i++) s[i][++cnt[i]] = i;
	scanf("%lld",&q);
	while(q--)
	{
		scanf("%lld",&x);
		while(true)
		{
			int now = s[x][cnt[x]];
			//x 表示最开始时的列数,now 表示当前的列数(即跳过的步数),cnt[x] 表示当前的行数 
			if(c[cnt[x]][now] == 'O') cnt[x]--;
			else if(cnt[x] >= n) break;
			else if(c[cnt[x] + 1][now] == 'X') break;
			else if(c[cnt[x] + 1][now] == '.') s[x][++cnt[x]] = now;
			else if(c[cnt[x]][now - 1] == '.' && c[cnt[x] + 1][now - 1] == '.') s[x][++cnt[x]] = now - 1;
			else if(c[cnt[x]][now + 1] == '.' && c[cnt[x] + 1][now + 1] == '.') s[x][++cnt[x]] = now + 1;
			else break;
		}
		c[cnt[x]][s[x][cnt[x]]] = 'O';
	}
	for(int i = 1;i <= n;i = -~i) printf("%s\n",(c[i] + 1));
	return 0;
}