The 2020 ICPC Asia Taipei-Hsinchu Site Programming Contest C. Pyramid

题目链接:
https://codeforces.com/gym/102835/problem/C
题意:
给你一个金字塔,从塔尖往下放小球,每个点有一个开关,可以让小球向左下或者向右下滑动,最开始金字塔的每个点的开关都是向左下的,每有一个小球经过,开关状态就进行翻转,给定金字塔的层数n,求第k个小球到塔底时位于哪一列。
题解:
对于任意一个开关,当有a个小球经过时,一定会有(a+1)/2个小球向左,a/2个小球向右(因为最开始的开关状态是向左下,奇数时左边会多一个,偶数时两边小球数目相同),这样我们就可以知道在一个小球到达某开关之前此处有多少小球经过,如果经过的小球是奇数时,这一点的开关就向右下方向,偶数时这一点的开关方向就向左下方向。因此,我们可以得出倒数第二排(k-1)个小球在不同点的分布状态。从而对于最后一个小球,可以通过除了最后一排以外,其他排的小球的分布情况得出最终答案。比如n=5,k=18,我们可以得到如下图的分布。
The 2020 ICPC Asia Taipei-Hsinchu Site Programming Contest C. Pyramid_第1张图片
之后我们就需要解决如何求出每一点小球经过的个数,但是我们可以知道n的最大数据范围是1e4,我们无法创建出一个二维数据进行计算求解,所以我们需要对于每一层进行逐步计算,我们可以开一个v[2][1e4]的数组,v[0][]和v[1][]依次表示上一行和当前行,比如v[0][]是第一行,而v[1][]就可以是第二行,再进行下一步计算时,v[0][]又可以是第三行,因为此时我们计算第三行只需要第二行,第一行已经没有用了,对于这一种操作,我们可以采取使用v[i%2][]和v[(i-1)%2][]的方式进行求解。我们处理了数组问题后,还需要记录上一个位置分出来一半的小球的数量。这里有个地方需要注意,就是下一层都比上一层多一列,这时我们在循环时就不需要多余考虑,可以理解为这一层第2列的个数加1的一半可以直接加在下一层的2列的位置,个数加1的一半是因为我们只进行了向左下滑动的操作。(从上一层的第2列到下一层的第2列其实是向左下滑动),向右下滑动也是同样的道理。
时间复杂度O(n^2)

上代码:

#pragma GCC optimize(2)
#include 
using namespace std;
typedef long long ll;
const int maxn=1e4+5;
const int mod=1e9+7;
ll m,n,i,j,ans,k;
int main()
{
    ll t;cin>>t;
    while(t--)
    {
    	ll v[2][maxn];
    	ans=0;
    	cin>>n>>k;
    	if(n==1)
    	{
    		cout<<0<<endl;
    		continue;
		}
		v[0][0]=k-1;
		if(v[0][0]%2)
			ans++;
		for(ll i=1;i<=n-2;i++)
		{
			ll cnt=0,tt=0;
			for(ll j=0;j<i;j++)
			{
				v[i%2][cnt++]=tt+(v[(i-1)%2][j]+1)/2;
				tt=v[(i-1)%2][j]/2;
			}
			v[i%2][cnt]=tt;
			if(v[i%2][ans]%2)
				ans++;
		}
		cout<<ans<<endl;
	}
	return 0;
}

你可能感兴趣的:(The 2020 ICPC Asia Taipei-Hsinchu Site Programming Contest C. Pyramid)