「SCOI 2019 D1T1」跳跃游戏

传送门


problem

m m m 个小球, n n n 个岛。其中第一个、最后一个岛以及中间的第 k k k 个岛 ( 1 < k < n ) (1< k< n) (1<k<n) 是固定的,其余岛是悬浮的。一开始所有小球都在第一个岛,你的目标是把他们都移动到最后一个岛,并且使用的步数尽量小。

每回合你可以移动一个小球到它左边或者右边的那个平台,但是有一些限制:

  1. 移动的小球必须是移动前它所在的平台上编号最小的;
  2. 移动的小球必须是移动后它所在的平台上编号最小的;
  3. 如果一个小球移动到了一个浮动平台上,那下一回合必须将这个小球移走,否则它就会掉下去。

q q q 个询问,每次给出一个时刻,你需要回答在最优方案中,这个时刻正在跳跃的是哪个小红球。

保证询问给出的时刻小于等于最优方案数,且保证答案小于等于 30 30 30

数据范围: n ≤ 1 0 8 n≤10^8 n108 m ≤ 1000 m≤1000 m1000 q ≤ 5000 q≤5000 q5000


solution

这应该就是汉诺塔问题的变种。

如果有 i i i 个点,那么最优方案为 f i = 3 f i − 1 + ( n − 1 ) f_i=3f_{i-1}+(n-1) fi=3fi1+(n1)。而 m ≤ 1000 m\le 1000 m1000,貌似直接高精是不行的。

考虑打个表找规律(或者自己手推),如下:

(n-1)1
(k-1)2
(n-1)1
(n-k)2
(n-1)1
(k-1)3
(n-1)1
(n-k)2
(n-1)1
(k-1)2
(n-1)1
(n-k)3
...

如果再打多点,会发现:

  • 对于个数,发现是 ( n − 1 ) (n-1) (n1) ( k − 1 ) (k-1) (k1) ( n − 1 ) (n-1) (n1) ( n − k ) (n-k) (nk) 的循环。
  • 对于数字,发现:
    答案为 1 1 1 的时候是奇数行;
    答案为 2 2 2 的时候是行为 2 2 2 的倍数且不为 6 6 6 的倍数;
    答案为 3 3 3 的时候是行为 6 6 6 的倍数且不为 18 18 18 的倍数;
    答案为 4 4 4 的时候是行为 18 18 18 的倍数且不为 54 54 54 的倍数;
    答案为 5 5 5 的时候是行为 54 54 54 的倍数且不为 162 162 162 的倍数;
    ⋯ ⋯ \cdots\cdots

由于 2 × 3 30 2\times 3^{30} 2×330 大约是 1 0 14 10^{14} 1014 左右,是在 long long 范围的,可以预处理一下。

然后就可以用高精做了,时间复杂度 O ( q × l e n × 30 ) O(q\times len\times 30) O(q×len×30)


code

#include
#define ll long long
using namespace std;
int n,m,k,q;
struct Bignum{
	int n,num[505];
	Bignum()  {n=0,memset(num,0,sizeof(num));}
	friend Bignum operator+(const Bignum &A,const Bignum &B){
		Bignum C;C.n=max(A.n,B.n);
		for(int i=1;i<=C.n;++i){
			C.num[i]+=A.num[i]+B.num[i];
			if(C.num[i]>9)  C.num[i]-=10,C.num[i+1]++;
		}
		if(C.num[C.n+1])  C.n++;
		return C;
	}
	friend Bignum operator/(const Bignum &A,ll b){
		ll ans=0;Bignum C;C.n=A.n;
		for(int i=C.n;i;--i){
			ans=ans*10+A.num[i];
			if(ans>=b)  C.num[i]=ans/b,ans=ans-(ans/b)*b;
		}
		while(C.n&&!C.num[C.n])  C.n--;
		return C;
	}
	friend ll operator%(const Bignum &A,ll b){
		ll ans=0;
		for(int i=A.n;i;--i){
			ans=ans*10+A.num[i];
			if(ans>=b)  ans=ans-(ans/b)*b;
		}
		return ans;
	}
	void Read(){
		char c=getchar();n=0;
		while(!isdigit(c))  c=getchar();
		while( isdigit(c))  num[++n]=c^48,c=getchar();
		reverse(num+1,num+n+1);
	}
}One,p;
ll Mod,f[35];
void init(){
	f[1]=1,f[2]=2;
	for(int i=3;i<=31;++i)  f[i]=3ll*f[i-1];
	One.n=1,One.num[1]=1;
}
void adjust(){
	if(Mod>0)  p=p+One;
	if(Mod>n-1)  p=p+One;
	if(Mod>n-1+k-1)  p=p+One;
	if(Mod>n-1+k-1+n-1)  p=p+One;
}
int main(){
	init();
	scanf("%d%d%d%d",&n,&m,&k,&q);
	int tmp=3*n-3; //(n-1)+(k-1)+(n-1)+(n-k)
	while(q--){
		p.Read(),Mod=p%tmp;
		p=p/tmp,p=p+p,p=p+p;
		adjust();
		for(int i=1;i<=30;++i)
			if(p%f[i]==0&&p%f[i+1]!=0)  {printf("%d\n",i);break;}
	}
	return 0;
}

你可能感兴趣的:(#,高精度,打表,高精度)