【CF538G】Berserk Robot(思维)

点此看题面

  • 有一个二维平面,一个机器人从原点出发。
  • 重复执行一个长度为(m)的操作序列(有上下左右四种操作)。
  • 给出机器人(n)个时刻的坐标,求一个可能的操作序列。
  • (nle2 imes10^5,mle2 imes10^6)

二维化一维

第一步转化就很妙。

考虑我们把坐标系旋转(45^circ),那么就变成了左上、左下、右上、右下四个方向。

然后发现每次操作横纵坐标各自改变(1),且二者相互独立。

因此我们可以把它当作两个相互独立的问题分别求解,问题一下简单了很多。

三元组((x_i,A_i,B_i))

(S_i)(i)次操作后位置总变化量。(其实就是前(i)次操作位置变化量的前缀和)

假设已知(T_i)时刻坐标为(B_i)

(A_i=T_i exttt{div} m,x_i=T_i exttt{mod} m),显然第(B_i=A_i imes S_m+S_{x_i})

基础的移项得到(S_{x_i}=-A_i imes f_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 iin[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}) imes S_m+(B_{i-1}-B_i)|le x_i-x_{i-1} ]

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

[x_{i-1}-x_ile (A_i-A_{i-1}) imes 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}) imes S_mle (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_i-S_{i-1} ]

简单移项化简得到:

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

那么我们直接把它们之间前(a)个位置填成(1),后面填成(-1)即可。

显然(x)不能为小数,因此要注意,尽管理论上(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;//把两个一维拼起来得到二维答案
}
原文地址:https://www.cnblogs.com/chenxiaoran666/p/CF538G.html