「JOISC2021」IOI Fever
作者:互联网
题目
点这里看题目。
分析
唉,感觉分析起来力不从心啊......
首先做一些简单的规约处理:
-
将居民 \(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