[CCO2022] Double Attendance 题解
Aaron_Romeo · · 题解
小数肯定做不了,但是注意到将所有区间
心路历程
想直接看正解就跳过。
贪心应该不好做,最长路还不如 dp。不如就考虑 dp。
比如说将所有点离散化一下,
然后再考虑解决这个问题,比如说每两个点之间我只记录片看得最多的那个人,看得片一样多就取最快的。仔细想想发现可能不优(原因这里因为我懒所以不多赘述)。
接着就不会了。看了看题解,发现要将离散化连根刨掉。
状态还是
转移很简单,时间复杂度可以做到
如何优化?
显然对于同一
而
正解
重新定义一下状态,
转移的话考虑看的下一部片在哪个教室。如果在当前教室就直接跳,否则在另一个教室那么现在就出发肯定是最优的(注意可能在当前教室再向前走几步可以使得
然后用二分维护一下转移就做完了,更多细节见代码。
Code
#include <iostream>
#include <algorithm>
#include <cstring>
typedef long long ll;
const int N = 3e5;
const int INF = 0x3f3f3f3f;
#define gc() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1000000, stdin), p1 == p2) ? EOF : *p1 ++ )
inline bool is_num(char ch) {return '0' <= ch && ch <= '9';}
inline bool is_char(char ch) {return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z';}
char buf[1000005], *p1 = buf, *p2 = buf;
inline int read()
{
char ch;
int f = 1, x = 0;
while (!is_num(ch = gc())) if (ch == '-') f = -1;
do x = (x << 1) + (x << 3) + ch - '0'; while (is_num(ch = gc()));
return f * x;
}
int n[2], m;
int dp[2][2][2];
struct node
{
int l, r;
bool operator < (const node &t) const {return l < t.l;}
bool operator < (const int &t) const {return r < t;}
} block[2][N + 5];
inline int get(int type, int pos) {return std :: lower_bound(block[type] + 1, block[type] + n[type] + 1, pos) - block[type];}
inline int find(int type, int pos) {int t = get(type, pos); return block[type][t].l <= pos ? t : 0;} // 在 type 教室的 pos 位置放的是哪部片子,没放返回 0
inline int next(int type, int pos) {int t = get(type, pos); return t + (block[type][t].l <= pos);} // 在 type 教室的 pos 位置的下一部片是谁
inline void modify(int &x, int y) {x = std :: min(x, y);}
int main()
{
n[0] = read() + 1, n[1] = read() + 1, m = read();
for (int i = 1; i < n[0]; i ++ ) block[0][i] = /* */ {read(), read() - 1}; block[0][n[0]] = /* */ {INF, INF}; std :: sort(block[0] + 1, block[0] + n[0] + 1);
for (int i = 1; i < n[1]; i ++ ) block[1][i] = /* */ {read(), read() - 1}; block[1][n[1]] = /* */ {INF, INF}; std :: sort(block[1] + 1, block[1] + n[1] + 1);
std :: memset(dp, 0x3f, sizeof dp);
dp[!block[0][1].l][0][0] = 0;
int res = 0;
for (int i = !block[0][1].l; i <= n[0] + n[1]; i ++ )
{
std :: memset(dp[i + 1 & 1], 0x3f, sizeof dp[i + 1 & 1]);
for (int cas = 0; cas <= 1; cas ++ ) // 注意因为可能会出现 i 更新 i, 所以要么选择更新两遍,要么类似于 dijkstra 将值从小到大更新
for (int j = 0; j <= 1; j ++ )
for (int k = 0; k <= 1; k ++ )
{
if (dp[i & 1][j][k] >= INF) continue;
int tim = dp[i & 1][j][k], pos = find(j, tim), across = find(j ^ 1, tim + m), after = next(j, tim);
modify(dp[i + (across && !k) & 1][j ^ 1][pos && find(j, tim + 2 * m) == pos], tim + m); // 下一部片在另一个教室,立即出发
modify(dp[i + 1 & 1][j][across && k && find(j ^ 1, block[j][after].l + m) == across], block[j][after].l); // 下一部片在当前教室
res = std :: max(res, i);
}
}
printf("%d", res);
return 0;
}