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";
}