【CF538G】Berserk Robot(思维)
- 有一个二维平面,一个机器人从原点出发。
- 重复执行一个长度为\(m\)的操作序列(有上下左右四种操作)。
- 给出机器人\(n\)个时刻的坐标,求一个可能的操作序列。
- \(n\le2\times10^5,m\le2\times10^6\)
二维化一维
第一步转化就很妙。
考虑我们把坐标系旋转\(45^\circ\),那么就变成了左上、左下、右上、右下四个方向。
然后发现每次操作横纵坐标各自改变\(1\),且二者相互独立。
因此我们可以把它当作两个相互独立的问题分别求解,问题一下简单了很多。
三元组\((x_i,A_i,B_i)\)
设\(S_i\)为\(i\)次操作后位置总变化量。(其实就是前\(i\)次操作位置变化量的前缀和)
假设已知\(T_i\)时刻坐标为\(B_i\)。
令\(A_i=T_i\ \texttt{div}\ m,x_i=T_i\ \texttt{mod}\ m\),显然\(B_i=A_i\times S_m+S_{x_i}\)。
基础的移项得到\(S_{x_i}=-A_i\times S_m+B_i\)。
这样我们得到了若干\((x_i,A_i,B_i)\)的三元组,可以把它们按照\(x_i\)排个序。同时在首尾分别加上\((0,0,0)\)和\((m,-1,0)\)(分别代表\(S_0=0\)和\(S_m=S_m\)),方便后续处理。
然后问题就是要构造一个合法的\(S\)序列,满足\(\forall i\in[1,m],|S_i-S_{i-1}|=1\)。
那么其实我们只要先求出\(S_m\)的取值范围就可以了。
求\(S_m\):绝对值相关的简单数学推导
于是我们考虑每对相邻的三元组,它们之间需要满足:
显然后面一项正负性已知,\(|x_{i-1}-x_i|=x_i-x_{i-1}\)。
而对于前一项,我们代入\(S_{x_i}\)的值得到:
数学老师告诉我们,这个式子恒等于:
移个项:
然后就是根据\(A_i-A_{i-1}\)的正负性分类讨论一下,把这个系数除掉,就可以得到\(S_m\)的一个取值区间。
对所有取值区间求个交集就是\(S_m\)最终的取值范围了。
构造合法序列
对于两个相邻的三元组,设它们之间有\(a\)个\(1\),那么就有\(x_i-x_{i-1}-a\)个\(-1\),所以:
简单移项化简得到:
那么我们直接把它们之间前\(a\)个位置填成\(1\),后面填成\(-1\)即可。
显然\(a\)不能为小数,因此要注意,尽管理论上\(S_m\)可以在取值范围内任取,但它的奇偶性可能影响到这个式子中分母的奇偶性,所以我们要把最小值\(L\)和\(L+1\)都作为\(S_m\)试一遍。
代码:\(O(nlogn+m)\)
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 200000
#define M 2000000
#define LL long long
#define eps 1e-12
using namespace std;
int n,m,t1[M+5],t2[M+5];
struct Data
{
int x;LL A,B;I Data(CI t=0,LL a=0,LL b=0):x(t),A(a),B(b){}
I bool operator < (Con Data& o) Con {return x<o.x;}//根据x排序
}s1[N+5],s2[N+5];
LL f[N+5];I bool Try(Data* s,int* t,CI x)//f[i]指题解中的S[x[i]]
{
RI i,j;LL k;for(i=1;i<=n+1;++i) f[i]=s[i].B-s[i].A*x;for(i=1;i<=m;++i) t[i]=0;//计算每个f,清空t
for(i=1;i<=n+1;++i) if((k=(f[i]-f[i-1])+(s[i].x-s[i-1].x))&1)//计算两个位置间有多少1
return 0; else for(k>>=1,j=1;j<=k;++j) t[s[i-1].x+j]=1;return 1;//是小数则非法,否则填上1
}
I void Work(Data* s,int* t)//求解一维答案
{
#define NA() (puts("NO"),exit(0))//无解
RI i,j;LL L=-m,R=m;for(sort(s+1,s+n+1),s[n+1]=Data(m,-1,0),i=1;i<=n+1&&L<=R;++i)
{
if(s[i-1].A==s[i].A) {if(abs(s[i-1].B-s[i].B)>s[i].x-s[i-1].x) NA();}//A[i]-A[i-1]=0
else s[i-1].A<s[i].A?//A[i]-A[i-1]>0
(
L=max(L,(LL)ceil(1.0L*((s[i-1].x-s[i].x)-(s[i-1].B-s[i].B))/(s[i].A-s[i-1].A)-eps)),
R=min(R,(LL)floor(1.0L*((s[i].x-s[i-1].x)-(s[i-1].B-s[i].B))/(s[i].A-s[i-1].A)+eps))
)://A[i]-A[i-1]<0
(
L=max(L,(LL)ceil(1.0L*((s[i].x-s[i-1].x)-(s[i-1].B-s[i].B))/(s[i].A-s[i-1].A)-eps)),
R=min(R,(LL)floor(1.0L*((s[i-1].x-s[i].x)-(s[i-1].B-s[i].B))/(s[i].A-s[i-1].A)+eps))
);
}
if(L>R||(!Try(s,t,L)&&(L+1>R||!Try(s,t,L+1)))) NA();//分别尝试L和L+1
}
int main()
{
RI i;LL t,x,y;for(scanf("%d%d",&n,&m),i=1;i<=n;++i)
scanf("%lld%lld%lld",&t,&x,&y),s1[i]=Data(t%m,t/m,x-y),s2[i]=Data(t%m,t/m,x+y);//坐标系转45°
for(Work(s1,t1),Work(s2,t2),i=1;i<=m;++i)
putchar(t1[i]?(t2[i]?'R':'D'):(t2[i]?'U':'L'));return 0;//把两个一维拼起来得到二维答案
}