【题目链接】
- BZOJ
- UOJ
【思路要点】
- 将所有点按照纵坐标排序,分别处理同一纵坐标的点。
- 显然,每个点在各个方向上的后继点若存在,是唯一的,先预处理。
- 记\(f_i\)表示从节点\(i\)出发,能够经过的最多的点数。
- 若不考虑左右的移动,\(f_i\)就是\(i\)在三个方向上后继结点的\(f\)值的最大值加1,记这个值为\(tmp_i\)。
- 不妨令同一纵坐标的点横坐标递增,那么有\(f_i=max\{tmp_i,max_{j=1}^{i-1}\{tmp_j+N-j+1\},max_{j=i+1}^{N}\{tmp_j+j\}\}\)。
- 显然后面两个部分可以用前/后缀最大值优化转移,这样就解决了问题的前两问。
- 考虑第三问,如果我们知道哪些边可能会出现在前面问题的最优解上,那么我们可以通过有上下界的有源有汇最小流来解决问题,问题在于判断哪些边可能会出现在前面问题的最优解上。
- 再次DP,记\(g_i\)表示从原点走到点\(i\)至多经过的点数,若从原点出发无法到达\(i\),记\(g_i=-\infty\)。
- 若不考虑左右的移动,\(g_i\)就是\(i\)在三个方向上后继结点的\(f\)值的最大值加1,记这个值为\(tmp_i\)。
- 不妨令同一纵坐标的点横坐标递增,那么有\(g_i=max\{tmp_i,max_{j=1}^{i-1}\{tmp_j\}+i,max_{j=i+1}^{N}\{tmp_j\}+N-i+1\}\)。
- 显然后面两个部分可以用前/后缀最大值优化转移。
- 求出\(g_i\)后,考虑一条非左右方向的边\((x,y)\),它会出现在答案上当且仅当\(g_y+f_x=Ans\)。
- 至此,问题得到解决。时间复杂度\(O(NLogN+Dinic(N,N))\)。
【代码】
#include
using namespace std; const int MAXN = 50005; const int MAXP = 50005; const int INF = 1e8; template void chkmax(T &x, T y) {x = max(x, y); } template void chkmin(T &x, T y) {x = min(x, y); } template void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template void writeln(T x) { write(x); puts(""); } struct info {int x, y, home; }; struct edge {int dest; int flow; unsigned home; }; int s, t, olds, oldt, dist[MAXP]; vector b[MAXP]; unsigned curr[MAXP]; bool cmp(info a, info b) { if (a.y == b.y) return a.x < b.x; else return a.y > b.y; } info a[MAXN]; map up, lup, rup; int nxtup[MAXN], nxtlup[MAXN], nxtrup[MAXN]; int tmp[MAXN], ans[MAXN], method[MAXN], tot; int tnp[MAXN], bns[MAXN]; int dinic(int pos, int limit) { if (pos == t) return limit; int used = 0, tmp; for (unsigned &i = curr[pos]; i < b[pos].size(); i++) if (b[pos][i].flow != 0 && dist[pos] + 1 == dist[b[pos][i].dest] && (tmp = dinic(b[pos][i].dest, min(limit - used, b[pos][i].flow))) != 0) { used += tmp; b[pos][i].flow -= tmp; b[b[pos][i].dest][b[pos][i].home].flow += tmp; if (used == limit) return used; } return used; } bool bfs() { static int q[MAXP], l = 0, r = -1; for (int i = 0; i <= r; i++) dist[q[i]] = 0; q[l = r = 0] = s, dist[s] = 1; while (l <= r) { int tmp = q[l++]; for (unsigned i = 0; i < b[tmp].size(); i++) if (b[tmp][i].flow != 0 && dist[b[tmp][i].dest] == 0) { q[++r] = b[tmp][i].dest; dist[b[tmp][i].dest] = dist[tmp] + 1; } } return dist[t] != 0; } void addedge(int s, int t, int flow) { b[s].push_back((edge) {t, flow, b[t].size()}); b[t].push_back((edge) {s, 0, b[s].size() - 1}); } void addedge(int x, int y) { addedge(x, y, INF); addedge(s, y, 1); addedge(x, t, 1); } int main() { int n; read(n); for (int i = 1; i <= n; i++) read(a[i].x), read(a[i].y), a[i].home = i; sort(a + 1, a + n + 1, cmp); int last = 0; for (int i = 1; i <= n; i++) { if (a[i].y == a[i + 1].y) continue; for (int j = last + 1; j <= i; j++) { tmp[j] = 0; if (up.count(a[j].x)) chkmax(tmp[j], up[a[j].x]); if (lup.count(a[j].x + a[j].y)) chkmax(tmp[j], lup[a[j].x + a[j].y]); if (rup.count(a[j].x - a[j].y)) chkmax(tmp[j], rup[a[j].x - a[j].y]); } static int pre[MAXN], suf[MAXN]; pre[last] = suf[i + 1] = -INF; for (int j = last + 1; j <= i; j++) pre[j] = max(pre[j - 1], tmp[j] - j); for (int j = i; j >= last + 1; j--) suf[j] = max(suf[j + 1], tmp[j] + j); for (int j = last + 1; j <= i; j++) { ans[j] = tmp[j]; chkmax(ans[j], pre[j - 1] + i); chkmax(ans[j], suf[j + 1] - (last + 1)); ans[j] += 1; up[a[j].x] = ans[j]; lup[a[j].x + a[j].y] = ans[j]; rup[a[j].x - a[j].y] = ans[j]; } last = i; } int ansval = 0; if (up.count(0)) chkmax(ansval, up[0]); if (lup.count(0)) chkmax(ansval, lup[0]); if (rup.count(0)) chkmax(ansval, rup[0]); writeln(ans[0] = ansval); up.clear(), lup.clear(), rup.clear(); last = 0; for (int i = 1; i <= n; i++) { if (a[i].y == a[i + 1].y) continue; for (int j = last + 1; j <= i; j++) { if (up.count(a[j].x)) nxtup[j] = up[a[j].x]; if (lup.count(a[j].x + a[j].y)) nxtlup[j] = lup[a[j].x + a[j].y]; if (rup.count(a[j].x - a[j].y)) nxtrup[j] = rup[a[j].x - a[j].y]; up[a[j].x] = j; lup[a[j].x + a[j].y] = j; rup[a[j].x - a[j].y] = j; } last = i; } nxtup[0] = up[0]; nxtlup[0] = lup[0]; nxtrup[0] = rup[0]; int now = 0; if (nxtup[0] && ans[nxtup[0]] == ansval) now = nxtup[0]; else if (nxtlup[0] && ans[nxtlup[0]] == ansval) now = nxtlup[0]; else if (nxtrup[0] && ans[nxtrup[0]] == ansval) now = nxtrup[0]; while (now != 0) { int l = now, r = now; while (a[l - 1].y == a[now].y) l--; while (a[r + 1].y == a[now].y) r++; static int pre[MAXN], suf[MAXN]; pre[l - 1] = suf[r + 1] = -INF; for (int i = l; i <= r; i++) pre[i] = max(pre[i - 1], tmp[i] - i); for (int i = r; i >= l; i--) suf[i] = max(suf[i + 1], tmp[i] + i); int tans = tmp[now]; chkmax(tans, pre[now - 1] + r); chkmax(tans, suf[now + 1] - l); if (tans == tmp[now]) { method[++tot] = a[now].home; if (ans[nxtup[now]] == tmp[now]) now = nxtup[now]; else if (ans[nxtlup[now]] == tmp[now]) now = nxtlup[now]; else if (ans[nxtrup[now]] == tmp[now]) now = nxtrup[now]; else now = 0; } else if (tans == pre[now - 1] + r) { for (int i = now; i <= r; i++) method[++tot] = a[i].home; for (int i = now - 1; i >= l; i--) { method[++tot] = a[i].home; if (tans == tmp[i] - i + r) { if (ans[nxtup[i]] == tmp[i]) now = nxtup[i]; else if (ans[nxtlup[i]] == tmp[i]) now = nxtlup[i]; else if (ans[nxtrup[i]] == tmp[i]) now = nxtrup[i]; else now = 0; break; } } } else { for (int i = now; i >= l; i--) method[++tot] = a[i].home; for (int i = now + 1; i <= r; i++) { method[++tot] = a[i].home; if (tans == tmp[i] + i - l) { if (ans[nxtup[i]] == tmp[i]) now = nxtup[i]; else if (ans[nxtlup[i]] == tmp[i]) now = nxtlup[i]; else if (ans[nxtrup[i]] == tmp[i]) now = nxtrup[i]; else now = 0; break; } } } } for (int i = 1; i <= tot; i++) printf("%d ", method[i]); printf("\n"); up.clear(), lup.clear(), rup.clear(); up[0] = lup[0] = rup[0] = 0; last = n + 1; for (int i = n; i >= 1; i--) { if (a[i].y == a[i - 1].y) continue; int l = i, r = last - 1; for (int j = l; j <= r; j++) { tnp[j] = -INF; if (up.count(a[j].x)) chkmax(tnp[j], up[a[j].x]); if (lup.count(a[j].x + a[j].y)) chkmax(tnp[j], lup[a[j].x + a[j].y]); if (rup.count(a[j].x - a[j].y)) chkmax(tnp[j], rup[a[j].x - a[j].y]); } static int pre[MAXN], suf[MAXN]; pre[l - 1] = suf[r + 1] = -INF; for (int j = l; j <= r; j++) pre[j] = max(pre[j - 1], tnp[j]); for (int j = r; j >= l; j--) suf[j] = max(suf[j + 1], tnp[j]); for (int j = l; j <= r; j++) { bns[j] = tnp[j]; chkmax(bns[j], pre[j - 1] + j - l); chkmax(bns[j], suf[j + 1] + r - j); bns[j] += 1; up[a[j].x] = bns[j]; lup[a[j].x + a[j].y] = bns[j]; rup[a[j].x - a[j].y] = bns[j]; } last = i; } s = n + 1, t = n + 2; olds = n + 3, oldt = n + 4; for (int i = 0; i <= n; i++) { int nxt = nxtup[i]; if (ans[nxt] + bns[i] == ansval) addedge(a[i].home, a[nxt].home); nxt = nxtlup[i]; if (ans[nxt] + bns[i] == ansval) addedge(a[i].home, a[nxt].home); nxt = nxtrup[i]; if (ans[nxt] + bns[i] == ansval) addedge(a[i].home, a[nxt].home); } for (int i = 0; i <= n; i++) { addedge(olds, i, INF); addedge(i, oldt, INF); } while (bfs()) { memset(curr, 0, sizeof(curr)); dinic(s, INF); } addedge(oldt, olds, INF); int finalans = 0; while (bfs()) { memset(curr, 0, sizeof(curr)); finalans += dinic(s, INF); } writeln(finalans); return 0; }