VP Educational Codeforces Round 15
Educational Codeforces Round 15
题意:求严格递增子数组的最长长度。
双指针经典题。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
int ans = 0;
for (int i = 0; i < n; ++ i) {
int j = i;
while (j + 1 < n && a[j + 1] > a[j]) {
++ j;
}
ans = std::max(ans, j - i + 1);
i = j;
}
std::cout << ans << "\n";
}
B. Powers of Two
题意:求数组中有多少对的和是\(2\)的幂。
存每个数的个数,然后枚举\(2\)的幂统计。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::map<i64, int> mp;
std::set<i64> s;
for (int i = 0; i < n; ++ i) {
i64 x;
std::cin >> x;
s.insert(x);
++ mp[x];
}
i64 ans1 = 0, ans2 = 0;
for (i64 i = 1; i <= 30; ++ i) {
for (auto & x : s) {
if (!s.count((1ll << i) - x)) {
continue;
}
if ((1ll << i) - x == x) {
ans1 += (i64)mp[x] * (mp[x] - 1) / 2;
} else {
ans2 += (i64)mp[x] * mp[(1ll << i) - x];
}
}
}
std::cout << ans1 + ans2 / 2 << "\n";
}
C. Cellular Network
题意:有\(n\)个城市和\(n\)个电台在一个坐标轴上,求电台传播半径最小是多少可以让每个城市至少被一个电台覆盖。
二分长度,check里用差分写,找每个电台可以覆盖的最小坐标的城市和最大坐标的城市。
点击查看代码
void solve() {
int n, m;
std::cin >> n >> m;
std::vector<i64> a(n), b(m);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
for (int i = 0; i < m; ++ i) {
std::cin >> b[i];
}
auto check = [&](i64 x) -> bool {
std::vector<int> d(n + 2);
for (int i = 0; i < m; ++ i) {
int l = std::lower_bound(a.begin(), a.end(), b[i] - x) - a.begin() + 1;
int r = std::upper_bound(a.begin(), a.end(), b[i] + x) - a.begin();
if (l <= r) {
++ d[l];
-- d[r + 1];
}
}
for (int i = 1; i <= n; ++ i) {
d[i] += d[i - 1];
if (d[i] <= 0) {
return false;
}
}
return true;
};
i64 l = 0, r = 2e9;
while (l < r) {
i64 mid = l + r >> 1ll;
if (check(mid)) {
r = mid;
} else {
l = mid + 1;
}
}
std::cout << l << "\n";
}
D. Road to Post Office
题意:你要走\(d\)公里,车子每公里用时\(a\),且每走\(k\)公里需要维护\(t\)秒。步行没有限制,每公里用时\(b\)。求到达终点的最小时刻。
如果我们枚举车子走多少米,然后剩下的步行,会超时。但发现如果开车更优,那么我们应该一直开车,最后一段步行,或者直接一直开车。那么就可以分情况讨论,如果\(d < k\),直接开车直达。发现我们看一直开车更优还是步行更优,注意步行前我们应该把能开车的距离用掉。
点击查看代码
void solve() {
i64 d, k, a, b, t;
std::cin >> d >> k >> a >> b >> t;
if (d < k) {
std::cout << a * d << "\n";
return;
}
i64 ans = k * a + (d - k) * b;
ans = std::min(ans, t * (d / k) + d * a);{
ans = std::min(ans, t * (d / k - 1) + (d - d % k) * a + b * (d % k));}
std::cout << ans << "\n";
}
E. Analysis of Pathes in Functional Graph
题意:有一个\(n\)点\(n\)边的有向图,每个点都恰好有一条出边。求每个点走\(k\)的距离的路径总和和路径上边权最小值。
可以考虑倍增,预处理\(son[i][j], sum[i][j], min[i][j]\)表示从\(i\)走\(2^j\)距离到达的点、路径总和,路径最小边权。那么对于每个点就按照\(2\)的幂从大到小枚举跳。
点击查看代码
void solve() {
int n;
i64 k;
std::cin >> n >> k;
std::vector<int> f(n), w(n);
for (int i = 0; i < n; ++ i) {
std::cin >> f[i];
}
for (int i = 0; i < n; ++ i) {
std::cin >> w[i];
}
const int inf = 2e9;
const int lg = std::__lg(k) + 1;
std::vector son(n, std::vector<int>(lg + 1));
std::vector sum(n, std::vector<i64>(lg + 1));
std::vector min(n, std::vector<i64>(lg + 1, inf));
for (int i = 0; i < n; ++ i) {
son[i][0] = f[i];
sum[i][0] = w[i];
min[i][0] = w[i];
}
for (int j = 1; j <= lg; ++ j) {
for (int i = 0; i < n; ++ i) {
son[i][j] = son[son[i][j - 1]][j - 1];
sum[i][j] = sum[i][j - 1] + sum[son[i][j - 1]][j - 1];
min[i][j] = std::min(min[i][j - 1], min[son[i][j - 1]][j - 1]);
}
}
for (int i = 0; i < n; ++ i) {
i64 ans1 = 0, ans2 = inf;
int u = i;
for (i64 j = lg; j >= 0; -- j) {
if (k >> j & 1) {
ans1 += sum[u][j];
ans2 = std::min(ans2, min[u][j]);
u = son[u][j];
}
}
std::cout << ans1 << " " << ans2 << "\n";
}
}