把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【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\):绝对值相关的简单数学推导

于是我们考虑每对相邻的三元组,它们之间需要满足:

\[|S_{x_{i-1}}-S_{x_i}|\le|x_{i-1}-x_i| \]

显然后面一项正负性已知,\(|x_{i-1}-x_i|=x_i-x_{i-1}\)

而对于前一项,我们代入\(S_{x_i}\)的值得到:

\[|(A_i-A_{i-1})\times S_m+(B_{i-1}-B_i)|\le x_i-x_{i-1} \]

数学老师告诉我们,这个式子恒等于:

\[x_{i-1}-x_i\le (A_i-A_{i-1})\times S_m+(B_{i-1}-B_i)\le x_i-x_{i-1} \]

移个项:

\[(x_{i-1}-x_i)-(B_{i-1}-B_i)\le (A_i-A_{i-1})\times S_m\le (x_i-x_{i-1})-(B_{i-1}-B_i) \]

然后就是根据\(A_i-A_{i-1}\)的正负性分类讨论一下,把这个系数除掉,就可以得到\(S_m\)的一个取值区间。

对所有取值区间求个交集就是\(S_m\)最终的取值范围了。

构造合法序列

对于两个相邻的三元组,设它们之间有\(a\)\(1\),那么就有\(x_i-x_{i-1}-a\)\(-1\),所以:

\[a-(x_i-x_{i-1}-a)=S_{x_i}-S_{x_{i-1}} \]

简单移项化简得到:

\[a=\frac{(S_{x_i}-S_{x_{i-1}})+(x_i-x_{i-1})}2 \]

那么我们直接把它们之间前\(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;//把两个一维拼起来得到二维答案
}
posted @ 2020-11-15 20:58  TheLostWeak  阅读(108)  评论(0编辑  收藏  举报