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";
}
posted @ 2025-03-19 15:32  maburb  阅读(41)  评论(0)    收藏  举报