题解:P10801 [CEOI2024] 海战

· · 题解

提供一篇码风友好,码量友好,题解语言友好易读的题解。

Solution

考虑一下碰撞情况,6 种碰撞方式。判断是否相撞(不考虑是否消失)的条件也很显然,例如南北方向就是横坐标相同,例如北东方向就是横纵坐标之和相同。具体的话可以参考 Code 部分的具体说明。

对于每种碰撞方式,维护它的所有可能情况。题目的难点在于碰撞完后船的消失会影响后面的碰撞情况。

以北东方向的碰撞为例,我们把所有北方向和东方向的船丢到一个 vector<int>() 中,按照横纵坐标之和为第一关键字,按照横坐标为第二关键字,从小到大排序。发现我们目前需要考虑的碰撞,是满足一下条件的船对:

其他方向的碰撞(南北、东西、北西、南东、南西)是同理的。

抓住可能产生真实碰撞的只在相邻的船对之间,所以考虑用链表动态维护碰撞序列(六种情况)。每次把可能产生贡献的碰撞丢到优先队列,然后不断取队头,删掉相撞的船即可。

补充一下时间复杂度方面的分析。初始我们会往优先队列里面放入 O(n) 个船对贡献。每次删除一艘船时,会产生至多一个新的船对贡献,依旧是 O(n)。所以本题复杂度是 O(n\log n),但是至少带 6 倍常数。

实现方面的细节,参考下文。

Code

\begin{array}{|c|c|c|c|}N(0)&S(1)&E(2)&W(3)\\NS(0)&NS(0)&NE(1)&NW(2)\\NE(1)&SE(3)&SE(3)&SW(4)\\NW(2)&SW(4)&EW(5)&EW(5)\end{array}

约定标号参考上表格。方向标号 [0,3],碰撞种类标号 [0,5]。具体每个碰撞情况的排序关键字就自己画图看看吧,这里不多说。

实现还有一个细节。当我们从优先队列取出碰撞删除时,要先把碰撞时间相同的全部取出来,再一起删,因为可能存在三艘船同时相撞的情况(样例二)。

````cpp #include<bits/stdc++.h> using namespace std; typedef long long ll; #define rep(i, a, b) for(int i = (a); i <= (b); ++i) #define per(i, a, b) for(int i = (a); i >= (b); --i) #define pii pair<int, int> #define pip pair<int, pii> #define fi first #define se second #define mkp make_pair const int maxn = 2e5 + 5; int n, vis[maxn]; struct node{ int tp; pii p, v[6]; pii dir[6]; }a[maxn]; vector<int> b[6]; int tr[4][3] = {{0, 1, 2}, {0, 3, 4}, {1, 3, 5}, {2, 4, 5}}; int NOW; inline bool cmp(int x, int y){ return a[x].v[NOW] < a[y].v[NOW]; } priority_queue<pip, vector<pip>, greater<pip> > pq; inline void calc(int o, int x, int y){ if(!x or !y or x > n or y > n or vis[x] or vis[y]) return; int X = a[x].v[o].se, Y = a[y].v[o].se; pii p = mkp(a[x].tp, a[y].tp); if(p.fi == p.se or a[x].v[o].fi != a[y].v[o].fi) return; if(p == mkp(1, 0) or p == mkp(3, 2)) pq.push(mkp((Y - X) >> 1, mkp(x, y))); if(p == mkp(0, 2) or p == mkp(3, 0) or p == mkp(1, 2) or p == mkp(3, 1)) pq.push(mkp(Y - X, mkp(x, y))); } inline void dlt(int x){ if(vis[x]) return; vis[x] = 1; rep(oo, 0, 2){ int o = tr[a[x].tp][oo]; int pre = a[x].dir[o].fi, suf = a[x].dir[o].se; a[pre].dir[o].se = suf, a[suf].dir[o].fi = pre; calc(o, pre, suf); } } signed main(){ ios_base::sync_with_stdio(0); cin.tie(NULL); cin >> n; rep(i, 1, n){ char ch; int x, y; cin >> x >> y >> ch; x = 1e9 - x; if(ch == 'S') a[i].tp = 1; if(ch == 'E') a[i].tp = 2; if(ch == 'W') a[i].tp = 3; a[i].p = mkp(x, y); if(!a[i].tp){ a[i].v[0] = mkp(x, y), a[i].v[1] = mkp(x + y, x), a[i].v[2] = mkp(x - y, x); } else if(a[i].tp == 1){ a[i].v[0] = mkp(x, y), a[i].v[3] = mkp(x - y, x), a[i].v[4] = mkp(x + y, x); } else if(a[i].tp == 2){ a[i].v[1] = mkp(x + y, x), a[i].v[3] = mkp(x - y, x), a[i].v[5] = mkp(y, x); } else{ a[i].v[2] = mkp(x - y, x), a[i].v[4] = mkp(x + y, x), a[i].v[5] = mkp(y, x); } rep(o, 0, 2) b[tr[a[i].tp][o]].push_back(i); } rep(o, 0, 5) NOW = o, sort(b[o].begin(), b[o].end(), cmp); rep(o, 0, 5){ int lst = -1; for(auto it = b[o].begin(); it != b[o].end(); ++it){ int x = *it; if(~lst) calc(o, lst, x); lst = x; if(it == b[o].begin()) a[x].dir[o].fi = 0; else a[x].dir[o].fi = *prev(it); if(next(it) == b[o].end()) a[x].dir[o].se = n + 1; else a[x].dir[o].se = *next(it); } } while(pq.size()){ vector<int> tmp; pip ss = pq.top(); tmp.clear(); while(pq.size() and pq.top().fi == ss.fi){ pip nw = pq.top(); pq.pop(); if(vis[nw.se.fi] or vis[nw.se.se]) continue; tmp.push_back(nw.se.fi), tmp.push_back(nw.se.se); } for(int x : tmp) dlt(x); } rep(i, 1, n) if(!vis[i]) cout << i << '\n'; return 0; } ````