其他分享
首页 > 其他分享> > 「JOISC2021」IOI Fever

「JOISC2021」IOI Fever

作者:互联网

题目

点这里看题目。

分析

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

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

Note.

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

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

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

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

那么,考虑出发点在 \((x_k,y_k)\) 的 \(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