VP Educational Codeforces Round 37 (Rated for Div. 2)
A. Water The Garden
题意:长度为\(n\)的直线上有\(k\)个点,第\(i\)个点的坐标为\(x_i\)。第\(t\)时刻第\(i\)个点会覆盖\([i - t + 1, i + t - 1]\)。求覆盖所有点最小时间。
可以二分加差分做。数据范围很小,也可以枚举\(t\)然后差分。
点击查看代码
void solve() {
int n, k;
std::cin >> n >> k;
std::vector<int> a(k);
for (int i = 0; i < k; ++ i) {
std::cin >> a[i];
}
auto check = [&](int t) -> bool {
std::vector<int> d(n + 2);
for (auto & x : a) {
int l = std::max(1, x - t + 1), r = std::min(n, x + t - 1);
++ 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;
};
int l = 1, r = n;
while (l < r) {
int mid = l + r >> 1;
if (check(mid)) {
r = mid;
} else {
l = mid + 1;
}
}
std::cout << l << "\n";
}
B. Tea Queue
题意:第\(i\)个人在\([l_i, r_i]\)时刻喝茶,每一时刻只有一个人可以喝茶,如果同一时刻有多个人可以喝茶则编号小的先喝。求每个人喝到茶的最小时间,如果喝不到答案是0。
模拟过去,记录一个\(t\)表示上一个人喝完茶后的时间。那么如果\(t \leq r_i, t = \max(t, l_i) + 1\)。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::vector<int> ans(n);
for (int i = 0, t = 1; i < n; ++ i) {
int l, r;
std::cin >> l >> r;
if (l >= t) {
t = l + 1;
ans[i] = l;
} else if (r >= t) {
ans[i] = t;
++ t;
} else {
ans[i] = 0;
}
}
for (int i = 0; i < n; ++ i) {
std::cout << ans[i] << " \n"[i == n - 1];
}
}
C. Swap Adjacent Elements
题意:给出一个排列,和一个字符串,如果\(s_i\)是1那么可以交换\(p_i, p_{i+1}\)。求能不能使数字升序。
可以发现以连续的1为一段,那么每一段是不相交的。那么把这些段进行排序,看是不是升序就行了。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
std::string s;
std::cin >> s;
for (int i = 0; i < n; ++ i) {
if (s[i] == '1') {
int j = i + 1;
while (j < n && s[j] == '1') {
++ j;
}
j = std::min(n - 1, j);
std::sort(a.begin() + i, a.begin() + j + 1);
i = j;
}
}
for (int i = 0; i + 1 < n; ++ i) {
if (a[i] > a[i + 1]) {
std::cout << "NO\n";
return;
}
}
std::cout << "YES\n";
}
D. Tanks
待补。
E. Connected Components?
题意:给你一个图,求这个图的补图的联通块数量以及每个联通块的点个数。
如果暴力做,我们可以枚举每个点,把和它没有边的点合并到一个集合。但这样会超时。
考虑寻找图里度数最小的点,这个点的度数最大为\(\frac{m}{n}\)。然后拿它做一次。那么只剩下\(\frac{m}{n}\)没有被合并,拿出来都操作一次,总共是\(\frac{m}{n} \times n = m\)的时间复杂度。
点击查看代码
struct DSU {
std::vector<int> fa, cnt;
DSU(int _n) {
init(_n);
}
void init(int _n) {
fa.assign(_n, 0);
cnt.assign(_n, 1);
std::iota(fa.begin(), fa.end(), 0);
}
int find(int x) {
return x == fa[x] ? x : fa[x] = find(fa[x]);
}
bool merge(int x, int y) {
x = find(x), y = find(y);
if (x == y) {
return false;
}
fa[y] = x;
cnt[x] += cnt[y];
return true;
}
bool same(int x, int y) {
return find(x) == find(y);
}
int size(int x) {
return cnt[find(x)];
}
};
void solve() {
int n, m;
std::cin >> n >> m;
std::vector<int> in(n);
std::vector<std::vector<int>> adj(n);
for (int i = 0; i < m; ++ i) {
int u, v;
std::cin >> u >> v;
-- u, -- v;
adj[u].push_back(v);
adj[v].push_back(u);
++ in[u], ++ in[v];
}
int u = 0;
for (int i = 1; i < n; ++ i) {
if (in[i] < in[u]) {
u = i;
}
}
std::vector<int> vis(n);
for (auto & v : adj[u]) {
vis[v] = 1;
}
DSU dsu(n);
for (int i = 0; i < n; ++ i) {
if (!vis[i]) {
dsu.merge(u, i);
}
}
for (int i = 0; i < n; ++ i) {
if (!vis[i]) {
continue;
}
std::vector<int> vis1(n);
for (auto & v : adj[i]) {
vis1[v] = 1;
}
for (int j = 0; j < n; ++ j) {
if (!vis1[j]) {
dsu.merge(i, j);
}
}
}
std::vector<int> ans;
for (int i = 0; i < n; ++ i) {
if (dsu.find(i) == i) {
ans.push_back(dsu.size(i));
}
}
std::sort(ans.begin(), ans.end());
std::cout << ans.size() << "\n";
for (auto & x : ans) {
std::cout << x << " ";
}
std::cout << "\n";
}
F. SUM and REPLACE
题意:定义\(d(i)\)为\(i\)的因子个数。给你一个数字,每次操作一个区间,询问区间和或者把区间每个\(a_i\)变为\(d(a_i)\)。
打表发现,一个数最多变\(6\)次就变成了1或者2。那么我们可以势能线段树。简单来说就是线段树区间操作改为每个点暴力操作,发现某个区间的数都操作超过6次就不操作。
点击查看代码
#define ls (u << 1)
#define rs (u << 1 | 1)
#define umid (tr[u].l + tr[u].r >> 1)
template <class Info, class Tag>
struct Node {
int l, r;
Info info;
Tag tag;
};
template <class Info, class Tag>
struct LazySegmentTree {
std::vector<Node<Info, Tag> > tr;
LazySegmentTree(int _n) {
init(_n);
}
LazySegmentTree(std::vector<Info> & a) {
int _n = (int)a.size();
init(_n, a);
}
void init(int _n) {
tr.assign(_n << 2, {});
build(0, _n - 1);
}
void init(int _n, std::vector<Info> & a) {
tr.assign(_n << 2, {});
build(0, _n - 1, a);
}
void pushup(int u) {
tr[u].info = tr[ls].info + tr[rs].info;
}
void pushdown(Node<Info, Tag> & u, Tag tag) {
u.info = u.info + tag;
u.tag = u.tag + tag;
}
void pushdown(int u) {
if (tr[u].tag.exist()) {
pushdown(tr[ls], tr[u].tag);
pushdown(tr[rs], tr[u].tag);
tr[u].tag.clear();
}
}
void build(int l, int r, int u = 1) {
tr[u] = {l, r, {}};
if (l == r) {
return;
}
int mid = l + r >> 1;
build(l, mid, ls); build(mid + 1, r, rs);
pushup(u);
}
void build(int l, int r, std::vector<Info> & a, int u = 1) {
tr[u] = {l, r, {}};
if (l == r) {
tr[u].info = a[l];
return;
}
int mid = l + r >> 1;
build(l, mid, a, ls); build(mid + 1, r, a, rs);
pushup(u);
}
void modify(int l, int r, Tag tag, int u = 1) {
if (tr[u].info.cnt >= 6) {
return;
}
if (tr[u].l == tr[u].r) {
pushdown(tr[u], tag);
return;
}
// pushdown(u);
int mid = umid;
if (l <= mid) {
modify(l, r, tag, ls);
}
if (r > mid) {
modify(l, r, tag, rs);
}
pushup(u);
}
Info query(int l, int r, int u = 1) {
if (l <= tr[u].l && tr[u].r <= r) {
return tr[u].info;
}
// pushdown(u);
int mid = umid;
if (r <= mid) {
return query(l, r, ls);
} else if (l > mid) {
return query(l, r, rs);
}
return query(l, r, ls) + query(l, r, rs);
}
// int query_first_not_appear(int u = 1) {
// if (tr[u].l == tr[u].r) {
// return tr[u].l;
// }
// pushdown(u);
// int mid = umid;
// if (tr[ls].info.sum != tr[ls].info.len) {
// return query_first_not_appear(ls);
// } else {
// return query_first_not_appear(rs);
// }
// }
};
struct Info {
i64 sum;
int cnt;
};
struct Tag {
int cnt;
bool exist() {
return cnt != 0;
}
void clear() {
cnt = 0;
}
};
const int N = 1e6 + 5;
int f[N];
Info operator + (const Info & a, const Info & b) {
Info res{};
res.sum = a.sum + b.sum;
res.cnt = std::min(a.cnt, b.cnt);
return res;
}
Info operator + (const Info & a, const Tag & b) {
Info res{};
res.sum = f[a.sum];
res.cnt = a.cnt + 1;
return res;
}
Tag operator + (const Tag & a, const Tag & b) {
Tag res{};
res.cnt = a.cnt + b.cnt;
return res;
}
void solve() {
for (int i = 1; i < N; ++ i) {
for (int j = i; j < N; j += i) {
f[j] += 1;
}
}
int n, m;
std::cin >> n >> m;
std::vector<int> a(n);
std::vector<Info> info(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
info[i] = {a[i], 0};
}
LazySegmentTree<Info, Tag> tr(info);
while (m -- ) {
int op, l, r;
std::cin >> op >> l >> r;
-- l, -- r;
if (op == 1) {
tr.modify(l, r, Tag{1});
} else {
std::cout << tr.query(l, r).sum << "\n";
}
}
}
G. List Of Integers
题意:求大于\(x\)的第\(k\)个与\(p\)互质的数。
考虑二分这个数,那么变成了求\([1, mid]\)里与\(p\)互质的数的个数。我们可以对\(p\)质因子分解,然后枚举每个选不选,容斥即可。
点击查看代码
void solve() {
i64 x, p, k;
std::cin >> x >> p >> k;
std::vector<i64> a;
for (int i = 2; i <= p / i; ++ i) {
if (p % i == 0) {
a.push_back(i);
while (p % i == 0) {
p /= i;
}
}
}
if (p > 1) {
a.push_back(p);
}
auto dfs = [&](auto & self, int u, int cnt, i64 sum, i64 n) -> i64 {
if (u == a.size()) {
return n / sum * (cnt % 2 ? -1 : 1);
}
return self(self, u + 1, cnt, sum, n) + self(self, u + 1, cnt + 1, sum * a[u], n);
};
i64 l = x + 1, r = 1e18, v = dfs(dfs, 0, 0, 1, x);
while (l < r) {
i64 mid = l + r >> 1ll;
if (dfs(dfs, 0, 0, 1, mid) - v >= k) {
r = mid;
} else {
l = mid + 1;
}
}
std::cout << l << "\n";
}

浙公网安备 33010602011771号