zoukankan      html  css  js  c++  java
  • 【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;//把两个一维拼起来得到二维答案
    }
    
  • 相关阅读:
    JQuery
    JS
    PHP
    Java并发编程:CopyOnWrite容器的实现
    Java并发编程:并发容器ConcurrentHashMap
    Java并发编程:同步容器
    Java并发编程:ThreadLocal
    Java并发编程:sleep、wait、yield对比
    Java并发编程:volatile关键字解析
    Java并发编程:Lock
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/CF538G.html
Copyright © 2011-2022 走看看