[ARC101D] Robots and Exits

题目

点这里看题目。

分析

比较典型的题目!

首先可以去掉:只能到达一个出口的机器人,它们不影响答案。

剩余的机器人就会分别有两个可用出口,我们可以记到左边一个的距离为 \(x\),右边一个距离为 \(y\),那么一个机器人就可以用平面上的点 \((x,y)\) 来表示。

分析移动过程,左、右移动相当于是将坐标轴沿直线 \(y=-x\) 平移,并且将首次接触的轴为 \(x\) 轴的点染为红色,首次接触的轴为 \(y\) 轴的点染为蓝色。不过由于染色只考虑首次接触,因此可以看作向上平移 \(x\) 轴或者向右平移 \(y\)

下面是重要环节,分析染色性质

考虑两个点 \((x_1,y_1)\)\((x_2,y_2)\),满足 \(x_1\le x_2,y_1\ge y_2\),就可以发现:

  • 如果 \((x_1,y_1)\) 是红色,那么 \((x_2,y_2)\) 一定是红色!
  • 对称地,如果 \((x_2,y_2)\) 是蓝色,那么 \((x_1,y_1)\) 一定是蓝色!

这两个性质都不难证明。反过来,对于满足这两条性质的染色方案,我们一定可以构造出对应的移动步骤,因此我们就找到了合法染色的充要条件。

更近一步,知道了红色的染法,剩余的蓝色也必然确定,因此我们只需要考虑红色即可。由于上述性质,红色的轮廓一定由排序后 \(y\) 的一组严格上升子序列(一条反链)确定,因此答案即严格上升子序列的个数。

细节:

  • 注意重复的点不能重复计算,两个点重合即代表它们是等价的;
  • 处理时,为了让点 \((x,y)\) 能够影响到的点全部位于它之后,因此我们需要将点按照第一维升序、第二维降序排序;

小结:

  • 对于包含两个信息的一个对象,通常都可以转化到平面上考虑,更加直观
  • 注意处理中的细节!

代码

#include <cstdio>
#include <utility>
#include <algorithm>
using namespace std;

#define rep( i, a, b ) for( int i = (a) ; i <= (b) ; i ++ )
#define per( i, a, b ) for( int i = (a) ; i >= (b) ; i -- )

const int mod = 1e9 + 7;
const int MAXN = 1e5 + 5, MAXM = 1e5 + 5, INF = 2e9;

template<typename _T>
void read( _T &x )
{
    x = 0; char s = getchar(); int f = 1;
    while( s < '0' || '9' < s ) { f = 1; if( s == '-' ) f = -1; s = getchar(); }
    while( '0' <= s && s <= '9' ) { x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar(); }
    x *= f;
}

template<typename _T>
void write( _T x )
{
    if( x < 0 ) putchar( '-' ), x = -x;
    if( 9 < x ) write( x / 10 );
    putchar( x % 10 + '0' );
}

template<typename _T>
_T MAX( const _T a, const _T b )
{
	return a > b ? a : b;
}

typedef pair<int, int> Point;

int BIT[MAXN];

Point pnt[MAXN];
int X[MAXN], Y[MAXN];
int N, M, tot, len = 0;

inline int Qkpow( int, int );
inline int Mul( int x, int v ) { return 1ll * x * v % mod; }
inline int Inv( const int a ) { return Qkpow( a, mod - 2 ); }
inline int Sub( int x, int v ) { return ( x -= v ) < 0 ? x + mod : x; }
inline int Add( int x, int v ) { return ( x += v ) >= mod ? x - mod : x; }
inline void Upt( int &x, const int v ) { x = Add( x, v ); }

inline void Up( int &x ) { x += ( x & ( -x ) ); }
inline void Down( int &x ) { x -= ( x & ( -x ) ); }
inline void Update( int x, int v ) { for( ; x <= len ; Up( x ) ) Upt( BIT[x], v ); }
inline int Query( int x ) { int ret = 0; for( ; x ; Down( x ) ) Upt( ret, BIT[x] ); return ret; }

bool Cmp( const Point &p, const Point &q ) 
{ return p.first == q.first ? p.second > q.second : p.first < q.first; }

inline int Qkpow( int base, int indx )
{
	int ret = 1;
	while( indx )
	{
		if( indx & 1 ) ret = Mul( ret, base );
		base = Mul( base, base ), indx >>= 1;
	}
	return ret;
}

void Discrete()
{
	static int val[MAXN];
	rep( i, 1, N ) val[++ len] = pnt[i].second;
	sort( val + 1, val + 1 + len ), len = unique( val + 1, val + 1 + len ) - val - 1;
	rep( i, 1, N ) pnt[i].second = lower_bound( val + 1, val + 1 + len, pnt[i].second ) - val; 
}

int main()
{
	read( N ), read( M );
	rep( i, 1, N ) read( X[i] );
	rep( i, 1, M ) read( Y[i] );
	tot = 0;
	rep( i, 1, N )
	{
		int p = lower_bound( Y + 1, Y + 1 + M, X[i] ) - Y;
		if( p > M || Y[p] == X[i] || p == 1 ) continue ;
		tot ++, pnt[tot].first = X[i] - Y[p - 1], pnt[tot].second = Y[p] - X[i];
	}
	int ans = 1, t;
	sort( pnt + 1, pnt + 1 + ( N = tot ), Cmp ), Discrete();
	rep( i, 1, N ) 
	{
		if( i > 1 && pnt[i].first == pnt[i - 1].first && pnt[i].second == pnt[i - 1].second ) continue;
		t = Add( Query( pnt[i].second - 1 ), 1 );
		ans = Add( ans, t ), Update( pnt[i].second, t );
	}
	write( ans ), putchar( '\n' );
	return 0;
}
posted @ 2021-06-18 16:57  crashed  阅读(51)  评论(0编辑  收藏  举报