P8462 「REOI-1」奶油蛋糕

PS:这是几天前我在洛谷月赛写的一道题,总共四道题,全对的就这一个(太菜了),其他的也就第二题能骗部分分(听说第二题是记忆化,但我还是没看懂,一直超时),下面只有简化题意,具体题意和样例测试详见洛谷,好的,下面我就写一下我的思路。

 

              拿到这题,还是跟以前一样,读题然后分析,(上面的大题干太长了,我也读了半天),大体环境是递加时间的,即若没有操作,一直运行的话,第1秒全体都加1,总和是1,第2秒全体都加2,总和是3,第3秒全体加3,总和是6…… 依次递推,但现在不是简单的加和问题,而是有操作步骤的,并且操作步骤还有条件。

              初始时,他会站在一个位置,然后随着时间每秒也进行不同方向的移动,并且他站在哪,那就不能随大环境加当前秒数,即停止加数,并在下一秒他离开该点时重新随秒数加数,只不过再加的不是秒数了,而是从1开始的自然数了,然后问你若干操作后,输出这个矩阵的各项的和的矩阵。怎么做,看一下数据,矩阵最大1000*1000,即1000000的数,若是暴力做,即每次移动后遍历一下这个矩阵然后进行处理肯定不行,数据量太大,那该怎么做。

              其实应该很多人跟我想都一样,那我在这里讲一下我的想法,暴力肯定寄,这个矩阵的形式,加上整体加和的外部大环境,我们应该特殊处理里面被走过的点,然后与外面的总和加和,即可得到正确答案。可能我说的有点抽象,我举个例子:例如这是个1000*1000的矩阵,但其中只有一点被走过(假设啊),因为被走过的点要慢于其他没被走过点的点(因为他一旦被走过就会从1开始计数,肯定低于当前时间秒数,也就低于其他人的和),那我只需要标记这点,并计算出他与其他点差了多少,到最后,我们遍历一次这个矩阵,若没被标记,则该点是秒数加和,否则是秒数加和减去其差值,这样就能快速得到答案了,那么接下来我们看一下这该如何实现。

            上述方法的难点无非在于如何计算出该点与其他没被遍历的点的差值,我们可以举些例子,下列分别为没被遍历的点的每秒加值和被遍历的点的每秒加值:

            没被:   1,  2,  3,  4,  5,  6,  7,  ……

            被:       0,  1,  2,  3,  4,  5,  6,  ……

我们可以发现,被遍历过的点,每秒与没被遍历过的点差值是固定的,都是1,我们再举例

           没被:    1, 2,  3, 4,  5,  6,  7, ……

           被:       1,  2,  3,  0,  1,  2,  3,  ……

这时我们发现虽然部分差值依然固定,但从1变成了4,这是为什么呢?

因为我们第一个样例是在第一秒遍历的,所以差1,而第二个样例是在第四秒遍历的,所以差4,所以我们发现,他的差值跟在第几秒遍历的有关,差值再乘时间不就是总差值了吗,看来我们里答案很近了    但是看下面一组样例:

          没被:     1,   2,   3,   4,   5,   6,   7,  ……

          被:        0,    1,   2,   0,   1,   2,   3,……

这组样例里,该点被遍历两次,第一次是在第一秒,此时差1,而到第四秒时,该点又被遍历,此时差值又变为4了,这就得特殊处理了,第一次遍历的总差值为3,而 3 = 1 * 3= 1 * ( 4 - 1 )

所以,当有多次遍历发生在同一点时,前一次的差值我们可以直接算出来,为当前秒数减去前一次遍历的秒数的差乘与前一次遍历的时间,这样我们的难题就解决了,下面是总体框架

         要建两个二维数组,一个存放上一次遍历的秒数,另一个记录该点与其他点的差,根据给定字符串按照题意进行行走,若该点没被遍历,则直接存入现在秒数,否则,先计算差值加到另一个数组,然后在存入当前秒数,最后遍历一次矩阵,如果没被遍历过,则该点的值为秒数求和,否则为秒数求和减去存在另一个数组的值,还要减去,最后还要减去最后一次遍历所带来的结果,即总秒数+1-最后一次遍历该点的秒数的差乘与最后一次遍历该点的秒数,因为这个我们没有算,,然后输出答案即可,下见代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll f[1010][1010],ans[1010][1010];   //f记录秒数,ans记录差值
ll sum,n,m,x,y,t=1;
string u;
int main ()
{
	cin>>n>>m>>x>>y;
	getchar();
	cin>>u;
	f[x][y]=1;                    //第一秒直接存数处理即可
	sum=m*(m+1)/2;                //计算总的秒数和
	for(int i=0;i<u.size();i++)
	{
		if(i==u.size()-1)         //依据题意,最后一次没有影响,直接跳过
		continue;
		t++;
		if(u[i]=='N')
		y++;
	    else if(u[i]=='S')
		y--;
		else if(u[i]=='W')
		x--;
		else if(u[i]=='E')
		x++;
		if(f[x][y]==0)
		f[x][y]=t;
		else
		{
			ans[x][y]-=(t-f[x][y])*f[x][y];     //记录差值,存进ans[]
			f[x][y]=t;                          //记录秒数
		}
	}
	for(int j=n;j>=1;j--)                      //左下角为(1,1),不是左上角了,注意坐标轴变换
	{
		for(int i=1;i<=n;i++)
		{
			if(f[i][j]==0)                     //没被遍历过
			cout<<sum;
			else
			cout<<sum+ans[i][j]-(m-f[i][j]+1)*f[i][j];
			if(i==n)
			cout<<endl;
			else
			cout<<" ";
		}
	}
	return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值