尼基塔计划度假时,决定好好享受一下并为自己买一双新鞋。为此,他研究了位于他酒店所在街道上的各家商店,并选择了 n 双鞋来试穿。尼基塔知道,每双鞋需要 k 秒钟来试穿,并打算每天安排 T 秒钟的时间来访问商店。街道是一个坐标轴,移动速度为每秒一单位,酒店位于原点,每次访问商店都必须从酒店出发并返回。求尼基塔试穿完所有他感兴趣的鞋子所需要的最少假期天数。
n≤104。
Solution
显然一个方案的时间只与最左和最右端点有关,所以我们可以把每次行走抽象成灾区间 [l,r] 可以选 c 个买。
int n; i64 k, t, a[kMaxN]; int f[kMaxN][kMaxN], fa[kMaxN][kMaxN], pre[kMaxN], nxt[kMaxN]; std::vector<int> vec[kMaxN];
inlinevoidchkmax(int &x, int y){ x = (x > y ? x : y); } inlinevoidchkmin(int &x, int y){ x = (x < y ? x : y); }
intcalc(int x, int y){ i64 s = llabs(a[x]) + llabs(a[y]) + llabs(a[y] - a[x]); if (s <= t) return std::min<i64>((t - s) / k, n); elsereturn0; }
intfind(int *fa, int x){ return x == fa[x] ? x : fa[x] = find(fa, fa[x]); }
voiddickdreamer(){ std::cin >> n >> k >> t; for (int i = 1; i <= n; ++i) std::cin >> a[i]; std::sort(a + 1, a + 1 + n); for (int i = 1; i <= n; ++i) { pre[i] = i + 1; for (int j = i; j; --j) { if (calc(j, i) >= i - j + 1) pre[i] = j; elsebreak; } nxt[i] = i - 1; for (int j = i; j <= n; ++j) { if (calc(i, j) >= j - i + 1) nxt[i] = j; elsebreak; } } for (int len = 1; len <= n; ++len) { for (int i = 1; i <= n - len + 1; ++i) fa[len][i] = i; } memset(f, 0x3f, sizeof(f)); for (int r = 1; r <= n; ++r) { for (int l = r; l; --l) { int c = calc(l, r); if (c >= r - l + 1) { f[l][r] = 1; } else { if (l == r) continue; int len = r - l + 1 - c; f[l][r] = std::min(f[nxt[l] + 1][r] + 1, f[l][pre[r] - 1] + 1); int pos = find(fa[len], l); chkmin(f[l][r], f[pos][pos + len - 1] + 1); // for (int i = l; i <= r - len + 1; ++i) chkmin(f[l][r], f[i][i + len - 1] + 1); } int len = r - l + 1; for (; vec[len].size(); vec[len].pop_back()) { int x = vec[len].back(); if (f[x][x + len - 1] < f[l][r]) break; fa[len][x] = l; } vec[len].emplace_back(l); } } std::cout << f[1][n] << '\n'; }