ARC103_B 题解

本篇题解有其它题解都没有的正确性证明部分,希望大家能给个赞,谢谢!

题意简述

初始位置设置在 (0,0),另给出 n(1n1000) 组形如 (x,y)(1|x|,|y|109) 的目标坐标。要求构造长度为 mm 自定,但要求 m40)的“步长”序列 c1,c2,,cmci 同样自定,要求 ci1012)。然后再对每一个目标坐标构造一个长度为 m 的字符串(由 LRUD 四种字符构成,分别表示左、右、上、下),使得从 (0,0) 开始走,第 i 步按字符串第 i 个字符所示的方向走长度 ci,在走 m 步后恰好能够走到该组目标坐标。

题目分析

ARC 的 B 题竟然是个紫的,好家伙

十分神奇的构造题。先判断无解的情况:设某一步走之前的坐标是 (x,y),走后的坐标是 (x,y),那么就有 x+y=x+y±ci,也就是说这一步走完后的四种情况的 x+y 的奇偶性相同。又因为每一组坐标走的步数相同,因此若题目中给出的目标坐标两两之间 x+y 的奇偶性不完全相同就说明无解。

那么如果坐标的 x+y 奇偶性完全相同就一定有解吗?答案是肯定的。想必大家看到题目后都能够想到 ci 的构造与“进制拆分”有关(多少有点小学奥数题的感觉)。本题就可以采用二进制和三进制表示两种方式,但使用三进制不如二进制方便,而且得到的答案也稍劣,所以本人采用的是二进制拆分。

由于一个数 x 的二进制表示是一定存在且唯一的,那么我们就有了这样的一个思路雏形:可以对 x 的每一个为 1 的二进制位分配一个对应的 2 的幂。

但这样有两个问题:由于每一步必须走,但是 xy 的二进制表示中对应的一位都是 0 怎么办?如果 xy 的二进制表示中某一位都是 1 又怎么办?

所以我们换一种思路,反过来从 (x,y)(0,0) 走。ci 直接取 2k,2k1,,4,2,1(2kmax(xi,yi))。特别地,如果 x+y 都是偶数就再添上一个 1 以保证最后能到达(0,0)。从最大的 2k 向小的扫,每次选择 x,y 当中距离当前扫到的 2 的幂更近的那个(即绝对值更大的),强行让它向 0 的方向走这一步并更新它自己的值(如果原来是 0,那么向随便一个方向走就行,本文以正向为例),不断进行下去直到 xy 减到 0 即可。这样构造出来 m=log2max(xi,yi)+232,满足题意。

这种方法的正确性其他题解貌似一笔带过了,在这里我稍微说一下我的思路:对最后的 cm=1x+y 为奇数)或者 cm1=1x+y 为偶数)操作完之后的 (x0,y0) 进行分析。在这里我们不妨设 x0,y00,|x0||y0|(其他情况类似)。只要我们证明 (x0,y0)=(0,0)x0+y0 为奇数,直接得到答案)或者 (1,0)x0+y0 为偶数,再用一个 1 就可以得到答案)即可。

使用反证法,分类讨论:

  1. y0=0,但 x0>1,那么反推操作之前的 x1,y1。若这一步是对 y1 进行操作,那么有 y1=±1,则 |y1|=1<|x1|,与选择绝对值大的进行操作矛盾!因此这一步只能是对 x1 进行操作,即 x1=x0±1。若 x1=x01,由 x0>1x1=x01>0x1>0 应该 1 才对,矛盾!因此我们得到 x1=x0+1>2。那么再往前一步的 x2,y2 仍然有 y2=0,x2=x1+2>4(证明过程类似)以及 y3=0,x3=x2+4>8……因此得到 yi=0,xi>2i。考虑第一步操作,应有 c1=2kmax(|x|,|y|),然而由刚刚得到的结论却有 x>2k,矛盾!因此这种情况不成立。

  2. x0y01,那么类似上面的过程,由每次选择 x,y 中绝对值更大的进行操作以及操作方向向 0 两个性质,我们仍然可以得到每次操作满足 xi+1=xi+2i 或者 yi+1=yi+2i。这样我们虽然不能确定 xiyi 的具体大小范围,但是可以确定 xi+yi2i+1。那么仍然考虑最初的 x,y2k,应有 2kmax(|x|,|y|)。但由刚刚的结论知 x+y2k+1+1,则有 max(|x|,|y|)2k+1+12>2k,矛盾!

综上,(x0,y0)=(0,0)(1,0),即这种方法一定能够得到解,是正确的。具体细节看代码。

代码实现

#include<bits/stdc++.h>
using namespace std;
int n,k,m;
long long x[1010],y[1010],c[42];
bool fg;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%lld%lld",&x[i],&y[i]);
k=max(k,max((int)log2(abs(x[i])-1),(int)log2(abs(y[i])-1))+1);//判断最大的 2^k 需要到多大
if(i==1)
fg=(x[i]+y[i])&1ll;//记录 x+y 的奇偶性
else if(((x[i]+y[i])&1ll)!=fg)//x+y 奇偶性不同,无解
{
printf("-1");
return 0;
}
}
for(long long i=k;i>=0;i--)
c[++m]=1ll<<i;//c[i] 直接取 2^k,2^(k-1)…4,2,1
if(!fg)//x+y 为偶数,再添加一个 1
c[++m]=1;
printf("%d\n",m);
for(int i=1;i<=m;i++)
printf("%lld ",c[i]);
printf("\n");
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(abs(x[i])>abs(y[i]))//取绝对值更大的那个
x[i]>=0?(printf("R"),x[i]-=c[j]):(printf("L"),x[i]+=c[j]);//向 0 的方向走
else
y[i]>=0?(printf("U"),y[i]-=c[j]):(printf("D"),y[i]+=c[j]);
}
printf("\n");
}
return 0;
}
posted @   Hadtsti  阅读(5)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端
点击右上角即可分享
微信分享提示