一个似乎和官方题解不太一样的一个做法。
首先考虑一个转化:假设连接两个车站的并不是一条轨道,而是一对单向的超距传送门。此时任意一辆车都可以从传送门的入口瞬移到出口,但是传送门改变方向需要 T 的时间。容易证明这样转化和原问题是等价的。
然后我们将发车时刻离散化,并进行一个贡献提前计算的 dp:记 fi,j,0/1 表示到第 i 个时刻,有 j 个车在等待发车,传送门此时的方向是从 A 到 B / 从 B 到 A,所有已经可以发车的车的等待时间总和的最小值。
转移有两种。第一种是不在第 i 个时刻开始改变传送门的方向,转移方程如下:
fi,j,k+j⋅(ti+1−ti)→fi,j+ci+1,1−k,k
其中 t 是第 i 个时刻对应的具体时刻(因为进行了离散化),ci,0/1 是在第 i 个时刻发车,方向是从 A 到 B / 从 B 到 A 的车的个数。直接转移的复杂度即为 O(N2)。
第二种是在第 i 个时刻 ti 时开始改变传送门的方向,到 ti+T 时完成改变,并立刻发出所有正在等待且方向正确的车;并立刻开始下一次改变,到 ti+2T 时将方向改变回来,并立刻发出所有正在等待且方向正确的车,以此类推重复反向若干次。
此时我们需要枚举反向的次数 c,并设 i′ 为第一个满足 ti′>ti+cT 的时刻。假设此时等待的车数和 原定在时刻 i 之后发车的车 总共产生的代价分别为 j′ 和 w,那么有如下转移:
fi,j,k+jT+w→fi′,j′,(k+c)mod2
其中 j′ 和 w 都可以在递增枚举 c 的过程中同步计算得到。
直接进行这个转移需要枚举 c,时间复杂度和值域有关,无法接受。但是我们不难发现:如果连续进行了两次反向,而这两次反向所占用的长度为 2T 的时间区间内不包含任何一个原定的发车时间点,那么我们一定不需要进行更多的连续反向。于是我们可以在遇到这种情况时停止枚举。这样 c 的枚举量下降到了 O(N)。
但是我们仍然需要枚举 i,j,c 三个值,复杂度为 O(N3)。注意到 w,j′ 都只和 i,c,k 有关,和 j 无关,因此我们可以先枚举 i,k,在枚举 c 之前预先求出最小的 fi,j,k+jT,再枚举 c 进行转移。这样复杂度就下降到了 O(N2),可以通过。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
| const int N = 5005; int n, psz, typ[N], idx[N], pos[N]; long long T, stt[N], pol[N], dp[N][N][2], cnt[N][2];
inline void Read() { cin >> n >> T; for (int i = 1;i <= n;i++) { char c; cin >> c >> stt[i]; pol[i] = stt[i]; typ[i] = c - 'A'; idx[i] = i; } }
inline void Prefix() { sort(idx + 1, idx + n + 1, [&](const int &x, const int &y) { return stt[x] < stt[y]; }); for (int i = 1;i <= n;i++) pol[i] = stt[idx[i]]; psz = unique(pol + 1, pol + n + 1) - pol - 1; pol[psz + 1] = 0x3f3f3f3f3f3f3f3f; }
inline void Upd(long long &x, long long y) { x = min(x, y); }
inline void Solve() { memset(dp, 0x3f, sizeof(dp)); for (int i = 1;i <= n;i++) cnt[pos[i] = lower_bound(pol + 1, pol + psz + 1, stt[i]) - pol][typ[i]]++; idx[n + 1] = n + 1; pos[idx[n + 1]] = psz + 1; dp[1][cnt[1][0]][1] = 0; dp[1][cnt[1][1]][0] = 0; for (int i = 1;i <= psz;i++) { for (int k = 0;k < 2;k++) { long long mnv = 0x3f3f3f3f3f3f3f3f; for (int j = 0;j <= n;j++) { Upd(mnv, dp[i][j][k] + j * T); if (i != psz || j == 0) Upd(dp[i + 1][j + cnt[i + 1][!k]][k], dp[i][j][k] + j * (pol[i + 1] - pol[i])); } int pi = 0; while (pi < n && stt[idx[pi + 1]] <= pol[i]) pi++; long long ct = pol[i]; int ck = k, w = 0, brf = 0; long long cst = 0; for (;;) { ct += T; int nxt = pi; cst += w * T; w = 0; while (nxt < n && stt[idx[nxt + 1]] <= ct) { nxt++; if (typ[idx[nxt]] == ck) w++; cst += ct - stt[idx[nxt]]; } if (nxt + 1 <= n) { Upd(dp[pos[idx[nxt + 1]]][w + cnt[pos[idx[nxt + 1]]][ck]][!ck], mnv + cst + (stt[idx[nxt + 1]] - ct) * w); } else { Upd(dp[psz + 1][w][!ck], mnv + cst); } if (pi == nxt) { brf++; if (brf >= 2) break; } else brf = 0; pi = nxt; ck = !ck; } } } cout << min(dp[psz + 1][0][0], dp[psz + 1][0][1]) << endl; }
int main() { Read(); Prefix(); Solve(); return 0; }
|