3983. 乾坤大挪移

63 篇文章 0 订阅
12 篇文章 0 订阅

Description

Input

输入文件move.in第一行个正整数 T,表示一共有T层需要计算的坐标及长度。
接下来T行每3个整数 X、Y、L,表示 每层钥匙坐标为 (X,Y),咒语中扩展出的X形状和十的长度为L,当然咒语不可能白念,所以 L>1。

Output

输出文件move.out中包含T行,对于每层棋盘输出一个整数 ANS,表示至少要念ANS个咒语才能完成任务。
如果无法完成任务,请输出 “Poor MLG!”

Sample Input

3
12 20 5
14 22 5
0 1 2

Sample Output

4
5
Poor MLG!

Data Constraint

对于 10%的数据, X、Y、L的绝对值 <=10^2,T<=5;
对于 40%的数据, X、Y、L的绝对值 <=10^16,T<=100;
对于 100%的数据, X、Y、L的绝对值 <=10^500,T<=1000;

Solution

高精度入门传送。

首先题意是多组数据,不是当前那层走不到后面就都走不到了。

然后我们发现简化题意之后即为从一个(x,y)走到(0,0)的最小步数。

令l=L-1

走的方式一:

(x,y)->(x\pm 2k,y)/(x,y\pm 2k)~~~~(0<k<=l)

方式二:

(x,y)->(x\pm k,y\pm k)~~~~~(0<k<=l)

首先x,y可以取绝对值。

观察上面走法,如果(x+y)是奇数那么无解,因为xy要么同为奇数或同为偶数。

发现斜着走不比横着或竖着走更优,并且斜着走最多只能走两步。

这里斜着走的定义为(x-len,y-len),因为x,y均为正数。长度为len

证明:

如果三次走的总长度(len之和)<2l,那么显然还不如用两次斜着走。

如果三次走的总长度(len之和)>=2l,那么可以先斜着走一次,剩下的用横着和竖着走两次。

扩展一下,对于所有的偶数次斜着走,都可以看成横着和竖着分别走,步数不会更劣。

扩展到所有奇数次斜着走,可以先斜着走一次,然后剩下的就变成偶数次斜着走的情况。同样不会更劣且斜着走的步数为1小于3。

因此横着竖着走不比斜着走更劣,斜着走最多走两次。

 

那么什么时候可以走两次?

当斜着走两次的len小于2l时,可能是会更优的,比如要从(11,11)走到(0,0),l=L-1=6时,最优答案是斜着走(x-6,x-6),再(x-5,x-5)走到(0,0),但是如果用了横着和竖着走的是不会优的。

 

接下来,具体操作如下:

先用第一种走法走到(x%2l,y%2l),统计步数,剩下来的最多只用走3步,讨论剩下的步数t的取值[0,3]。

接下来我们讨论所有可能的情况,其中a包含了不用第二种走法,其余用到第二种走法。

        a.x>=2l并且y>=2l。此时(x%2l,y%2l)中横纵坐标的其中一个可以少走一些步数,因为我们保证了横纵坐标之差为偶,因此一定可以让x%2l和y%2l的较小值少走一些步数使得两个坐标等于它们的较大值。此时直接用方式二走,t=max(x%2l,y%2l)/l上取整

        b.如果其中一个坐标>=2l,另一个<2l。考虑如果大于2l的坐标%2l之后还比<2l的坐标小,肯定能让它少走一点,使得它等于<2l的坐标,这样一定不会更劣。之后就转化到c的情况

        c.这里讨论的情况即xy坐标都固定不动(不能少走)的情况。如果两坐标中的较小值>=l,那么先让两坐标减去l,使得两坐标中较小值小于l,注意如果走完之后是奇数坐标就要让两个坐标在退回一步保证变成偶数(即x+1,y+1)。之后剩下的情况即为较小值小于l,那么我们再让两坐标减去两坐标的较小值(至少让一个变为0),剩下的再用一次2l走完(+1步)。

        d.t=x不为0则要走一步+y不为0则要走一步+(x,y)为奇数

至此,我们将所有情况讨论完,将之前的答案+t即为最终答案。

 

Code

#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cstdlib>
#define I int
#define ll long long
#define F(i,a,b) for(register I i=a;i<=b;i++)
#define Fd(i,a,b) for(register I i=a;i>=b;i--)
#define mem(a,b) memset(a,b,sizeof a)
#define N 1010
using namespace std;
I T,x[N],y[N],t[N],q[N],now[N],s[N],ss[N],r[N],l[N],c[N],d[N],e[N],ans[N],bz1,bz2,cnt1,cnt2;
char ch;
void R(I &w){
	w=0;ch=getchar();
	while(ch<'0'||ch>'9') ch=getchar();
	while(ch>='0'&&ch<='9'){w=w*10+ch-'0';ch=getchar();}
}
void in(I x[]){
	t[0]=0;
	ch=getchar();
	while(ch<'0'||ch>'9') ch=getchar();
	while(ch>='0'&&ch<='9'){t[++t[0]]=ch-'0';ch=getchar();}
	x[0]=t[0];
	F(i,1,x[0]) x[i]=t[x[0]-i+1];
}
void up(I x[]){
	F(i,1,x[0]){x[i+1]+=x[i]/10;x[i]%=10;}
	while(x[x[0]+1]){
		x[0]++;
		x[x[0]+1]+=x[x[0]]/10,x[x[0]]%=10;
	}
}
void add(I a[],I b[]){
	mem(t,0);
	t[0]=max(a[0],b[0]);
	F(i,1,t[0]) t[i]=a[i]+b[i];
	up(t);
}
void dec(I a[],I b[]){
	F(i,1,b[0]) a[i]-=b[i];
	F(i,1,b[0]){while(a[i]<0) a[i]+=10,a[i+1]--;}
	while(a[0]&&!a[a[0]]) a[0]--;
}
void mul(I a[],I val){F(i,1,a[0]) a[i]*=val;up(a);}
I cmp(I a[],I b[]){
	if(a[0]>b[0]) return 1;
	if(b[0]>a[0]) return 0;
	Fd(i,a[0],1){
		if(a[i]>b[i]) return 1;
		if(b[i]>a[i]) return 0;
	}
	return 1;
}
void div(I a[],I b[]){
	mem(q,0),mem(r,0);
	Fd(i,a[0],1){
		mul(r,10);r[1]+=a[i];up(r);
		Fd(j,9,0){
			mem(now,0);
			F(k,0,b[0]) now[k]=b[k];
			mul(now,j);
			if(cmp(r,now)){q[i]=j;dec(r,now);break;}
		}
	}
	q[0]=a[0];
	while(q[0]&&!q[q[0]]) q[0]--;
}
I main(){
	freopen("movement.in","r",stdin);
	freopen("movement.out","w",stdout);
	R(T);
	while(T--){
		mem(x,0),mem(y,0),mem(l,0);
		in(x),in(y),in(l);
		add(x,y);
		if(t[1]&1){printf("Poor MLG!\n");continue;} 
		l[1]--;
		F(i,1,l[0]) if(l[i]<0){l[i]+=10,l[i+1]--;}
		while(l[0]&&!l[l[0]]) l[0]--;
		
		mem(e,0);
		F(i,0,l[0]) e[i]=l[i];
		mul(e,2);up(e);
		bz1=bz2=1;
		div(x,e);
		if(cmp(x,r)&&cmp(r,x)) bz1=0;
		mem(x,0),mem(s,0);
		F(i,0,r[0]) x[i]=r[i];
		F(i,0,q[0]) s[i]=q[i];
		
		div(y,e);
		if(cmp(y,r)&&cmp(r,y)) bz2=0;
		mem(y,0),mem(ss,0);
		F(i,0,r[0]) y[i]=r[i];
		F(i,0,q[0]) ss[i]=q[i];
		
		if(!bz1&&bz2&&!cmp(y,x)) F(i,0,x[0]) y[i]=x[i];
		if(bz1&&!bz2&&!cmp(x,y)) F(i,0,y[0]) x[i]=y[i];
                //情况b
		add(s,ss);
		mem(ans,0);
		F(i,0,t[0]) ans[i]=t[i];
		cnt1=cnt2=0;
		cnt1=(x[0]>0)+(y[0]>0)+(x[1]&1||y[1]&1);
                //情况d
		if(bz1&&bz2){
			if(x[0]||y[0]){
				mem(s,0);
				if(cmp(x,y)) F(i,0,x[0]) s[i]=x[i];else F(i,0,y[0]) s[i]=y[i];
				//s=max(x,y);
				cnt2+=!cmp(l,s)?2:1;
			}
                        //情况a
		}
		else{
			mem(s,0);
			if(cmp(x,y)) F(i,0,y[0]) s[i]=y[i];else F(i,0,x[0]) s[i]=x[i];
                        //s=min(x,y);
			if(cmp(s,l)){
				dec(x,l);dec(y,l);cnt2++;
				if(x[1]&1){x[1]++;y[1]++;up(x),up(y);}
			}
			mem(s,0);
			if(cmp(x,y)) F(i,0,y[0]) s[i]=y[i];else F(i,0,x[0]) s[i]=x[i];
			//s=min(x,y);
			dec(x,s);dec(y,s);cnt2+=(s[0]>0)+(x[0]>0)+(y[0]>0);
                        //情况c
 		}
		ans[1]+=min(cnt1,cnt2);
		up(ans);
		printf("%d",ans[ans[0]]);
		Fd(i,ans[0]-1,1) printf("%d",ans[i]);
		printf("\n");
	}
	return 0;
}

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值