ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

「JOISC2021」IOI Fever

2022-07-01 23:03:29  阅读:158  来源: 互联网

标签:xPos Fever const int rep JOISC2021 yPos tim IOI


题目

点这里看题目。

分析

唉,感觉分析起来力不从心啊......

首先做一些简单的规约处理:

  • 将居民 \(1\) 平移到坐标原点;

  • 枚举一下居民 \(1\) 的行走方向,并且旋转坐标轴,使得 \(x\) 轴指向居民 \(1\) 的行走方向。

Note.

下面的坐标都是变换过后的坐标。

接下来,我们考虑一下每个居民怎么走才会最优。实际上,我们有这样一个简单的原则:不可能得病的方向就不要走

能这么想可能还真有点大病

怎么确定一个方向能不能得病呢?放宽一点条件,如果 \((x,y)\) 可以被传染到,显然在 \(t\) 时刻必须有 \(|x|+|y|\le t\)。

那么,考虑出发点在 \((x_k,y_k)\) 的 \(k\) 号居民:

  • 如果 \(|x_k|=|y_k|\):

    • \(x_k>0\),则 \(k\) 号居民应当主动靠近 \(1\) 号居民,也就是平行于 \(y\) 轴正方向行走。

      比如,如果 \(|x_k|=|y_k|\) 且 \((x_k,y_k)\) 都在第一象限,则向上走或者向右走一定不会减小 \(1\) 号居民和 \(k\) 号居民的曼哈顿距离。

      如果向左走,那么理论上 \(1\) 号居民可以和 \(k\) 号居民“恰好重合”,并且之后一直保持这个位置。但是实际上,由于 \(1\) 号居民第一步一定会向 \(x\) 轴正方向走,因此 \(1\) 号居民不可能影响到这个位置上的 \(k\) 号居民!

      Remark.

      就是这样!需要通过一些强有力的逻辑来产生强有力的结论!并且这里的推理是可以套用的有效模式。

      印证了之前的想法:一般情况下,(OI 中)都可以用一些相对较弱的条件、相对显然的原则生成相对较强的结论

      下面也可以类似地建立起一个分析,所以就不再赘述了。

    • \(x_k<0\),则 \(k\) 号居民应当平行于 \(x\) 轴正方向行走。分析方法类似。

  • 如果 \(|x_k|\neq |y_k|\):

    举个例子,比如 \(x_k>y_k\ge 0\)。则向上和向右走仍然是超级不优的。

    比较一下向左走和向下走。如果我们向下走,由于 \(x_k>y_k\),所以 \(1\) 号居民最多“追回” \(y_k\) 的时间,而这显然不足以让 \(1\) 号居民赶上 \(k\) 号居民并去影响它。所以,我们还只能向下走。

    类似地分析,我们得出结论:\(k\) 号居民会垂直地走向和 \((x_k,y_k)\) 距离更远的那条坐标轴。


上面的分析已经准确地指出了每个人应该的行走方向。因此,接下来就是一个纯纯的模拟过程。

显然是否染病随时间具有单调性。我们不妨设 \(t_u\) 为 \(u\) 最早感染的时间,而 \(s_{u,v}\) 表示 \(u,v\) 相遇的时间(不相遇就不管它)。

那么,当我们确定了某个 \(t_u\) 之后,我们可以将所有满足 \(s_{u,v}\ge t_u\) 的 \(t_v\) 全部用 \(s_{u,v}\) 来更新。这一点表明,按照时间顺序更新是不存在环的。因此,我们可以模仿 Dijkstra 算法,每次取出 \(t_u\) 最小的 \(u\) 并进行更新。

暴力执行是 \(O(n^2)\) 的,我们需要优化到 \(O(n\log n)\)。既然 Dijkstra 可以用堆优化,我们也尝试快速维护每次更新之后的最小堆。

有一点值得注意:在某个斜率的直线上来看,每次更新的范围虽然是一段区间,但它必然是一段前缀或后缀;此外,无论是前缀或者后缀,更新一定满足离中心越远则时间越大。因此,实际上一次范围更新后,当前看来有效的只有更新到的第一个点。当这个点被取出后,我们再加入范围更新中的下一个点。

总共只有 8 种可能的范围更新。对于每个点,它只需要接受某方向上 \(t\) 最小的一次范围更新,之后再遇到的一定不优于先前的更新。

也可以理解成,我们按照标记的方向进行了小小的拆点,本质上和之前的暴力差别不大。

Note.

注意 "Dominate" 的策略。

这样,时间复杂度就降到了 \(O(n\log n)\)。常数略大。

代码

#include <queue>
#include <cstdio>
#include <iostream>
#include <algorithm>

#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 INF = 1.5e9;
const int MAXN = 1e5 + 5;

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

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>
inline _T Min( const _T &a, const _T &b ) {
	return a < b ? a : b;
}

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

template<typename _T>
inline _T Abs( const _T &x ) {
	return x < 0 ? -x : x;
}

struct Node {
	int tim, id, slp, sgn;

	Node(): tim( INF ), id( 0 ), slp( 0 ), sgn( 0 ) {}
	Node( int T, int I, int S, int SN ): tim( T ), id( I ), slp( S ), sgn( SN ) {}

	inline bool operator < ( const Node &q ) const {
		return ! ( tim < q.tim );
	}
};

std :: priority_queue<Node> q;

int infSlp[4][3] = {
		{ 0, 2, 3 },
		{ 1, 2, 3 },
		{ 0, 2, 3 },
		{ 1, 2, 3 }
	},
	infDir[4][3] = {
		{ 2, 3, 1 },
		{ 3, 2, 0 },
		{ 0, 1, 3 },
		{ 1, 0, 2 }
	},
	infSgn[4][3] = {
		{ +1, +1, -1 },
		{ +1, +1, +1 },
		{ -1, -1, +1 },
		{ -1, -1, -1 }
	};

// 第一维是斜率,第二维是方向
// slope: 0=|,1=-,2=/,3=\;
// direc: 0=^,1=>,2=_,3=<;
std :: vector<std :: vector<int> > each[4][4], corVal[4][4];
std :: vector<int> tmpVec, tmpVal;

int tim[MAXN][9];
bool vis[MAXN][9];

int lay[4][MAXN], pos[4][MAXN];
int val[4][MAXN], seq[4][MAXN];

int xPos[MAXN], yPos[MAXN], dir[MAXN];

int N;

inline void TryUpt( const Node &nw ) {
	int hsh = nw.sgn == 1 ? nw.slp + 4 : nw.slp;
	if( vis[nw.id][hsh] ) return ;
	if( tim[nw.id][hsh] > nw.tim )
		tim[nw.id][hsh] = nw.tim, q.push( nw );
}

inline void Update( const int &u, const int &nTim ) {
	if( vis[u][8] ) return ;
	vis[u][8] = true, tim[u][8] = nTim;

	int p, d, s, cur, lim;
	std :: vector<int> *coord;
	rep( k, 0, 2 ) {
		d = infDir[dir[u]][k],
		s = infSlp[dir[u]][k];
		coord = &corVal[s][d][lay[s][u]];
		cur = corVal[s][dir[u]][lay[s][u]][pos[s][u]], lim = cur;
		if( infSgn[dir[u]][k] > 0 ) {
			lim += s > 1 ? ( nTim + 1 ) / 2 : nTim;
			p = std :: lower_bound( coord -> begin(), coord -> end(), lim ) - coord -> begin();
			if( p < ( int ) coord -> size() )
				TryUpt( Node( ( 1 + ( s > 1 ) ) * ( ( *coord )[p] - cur ), each[s][d][lay[s][u]][p], s, +1 ) );
		} else {
			lim -= s > 1 ? ( nTim + 1 ) / 2 : nTim;
			p = std :: upper_bound( coord -> begin(), coord -> end(), lim ) - coord -> begin() - 1;
			if( p >= 0 ) 
				TryUpt( Node( ( 1 + ( s > 1 ) ) * ( cur - ( *coord )[p] ), each[s][d][lay[s][u]][p], s, -1 ) );
		}
	}
}

inline void Clear() {
	rep( i, 1, N ) 
		rep( j, 0, 8 )
			tim[i][j] = INF,
			vis[i][j] = false;
	rep( a, 0, 3 ) rep( b, 0, 3 )
		each[a][b].clear(),
		corVal[a][b].clear();
	while( ! q.empty() ) q.pop();
}

int Calc() {
	Clear();
	dir[1] = 1;
	rep( i, 2, N ) {
		int a = Abs( xPos[i] ),
			b = Abs( yPos[i] );
		if( a == b ) {
			if( xPos[i] > 0 ) dir[i] = yPos[i] < 0 ? 0 : 2;
			else dir[i] = 1;
		} else {
			if( a > b ) dir[i] = xPos[i] > 0 ? 3 : 1;
			else dir[i] = yPos[i] > 0 ? 2 : 0;
		}
	}
	rep( i, 1, N ) {
		rep( j, 0, 3 ) seq[j][i] = i;
		val[0][i] = xPos[i];
		val[1][i] = yPos[i];
		val[2][i] = xPos[i] - yPos[i];
		val[3][i] = xPos[i] + yPos[i];
	}
	rep( j, 0, 3 ) {
		std :: sort( seq[j] + 1, seq[j] + 1 + N,
			[j] ( const int &a, const int &b ) -> bool {
				return val[j][a] < val[j][b];
			} );
		for( int l = 1, r ; l <= N ; l = r ) {
			for( r = l ; r <= N && val[j][seq[j][l]] == val[j][seq[j][r]] ; r ++ );
			if( j )
				std :: sort( seq[j] + l, seq[j] + r, 
					[] ( const int &a, const int &b ) -> bool {
						return xPos[a] < xPos[b];
					} );
			else
				std :: sort( seq[j] + l, seq[j] + r, 
					[] ( const int &a, const int &b ) -> bool {
						return yPos[a] < yPos[b];
					} );
			per( d, 3, 0 ) {
				tmpVec.clear(), tmpVal.clear();
				for( int k = l ; k < r ; k ++ )
					if( dir[seq[j][k]] == d ) {
						lay[j][seq[j][k]] = each[j][d].size();
						pos[j][seq[j][k]] = tmpVec.size();
						tmpVec.push_back( seq[j][k] );
						tmpVal.push_back( j == 0 ? yPos[seq[j][k]] : xPos[seq[j][k]] );
					}
				each[j][d].push_back( tmpVec );
				corVal[j][d].push_back( tmpVal );
			}
		}
	}
	tim[1][8] = 0, Update( 1, 0 );
	while( ! q.empty() ) {
		Node h = q.top(); q.pop();
		int u = h.id, d = dir[u];
		int s = h.slp, p = pos[s][u];
		int hsh = h.sgn == 1 ? h.slp + 4 : h.slp;
		if( ! vis[u][hsh] ) {
			vis[u][hsh] = true;
			if( h.sgn > 0 ) {
				if( p + 1 < ( int ) each[s][d][lay[s][u]].size() )
					TryUpt( Node( h.tim + ( 1 + ( s > 1 ) ) * ( corVal[s][d][lay[s][u]][p + 1] - corVal[s][d][lay[s][u]][p] ), 
								  each[s][d][lay[s][u]][p + 1], s, +1 ) );
			} else {
				if( p > 0 )
					TryUpt( Node( h.tim + ( 1 + ( s > 1 ) ) * ( corVal[s][d][lay[s][u]][p] - corVal[s][d][lay[s][u]][p - 1] ),
								  each[s][d][lay[s][u]][p - 1], s, -1 ) );
			}
		}
		Update( h.id, h.tim );
	}
	int ret = 0;
	rep( i, 1, N ) ret += tim[i][8] < INF;
	return ret;
}

int main() {
	read( N );
	rep( i, 1, N ) 
		read( xPos[i] ), read( yPos[i] );
	per( i, N, 1 )
		xPos[i] -= xPos[1],
		yPos[i] -= yPos[1];
	int ans = 0;
	rep( cas, 0, 3 ) {
		ans = Max( ans, Calc() );
		rep( i, 1, N )
			std :: swap( xPos[i], yPos[i] ), xPos[i] *= -1;
	}
	write( ans ), putchar( '\n' );
	return 0;
}

标签:xPos,Fever,const,int,rep,JOISC2021,yPos,tim,IOI
来源: https://www.cnblogs.com/crashed/p/16436168.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有