VP Educational Codeforces Round 36 (Rated for Div. 2)


A. Garden

题意:\(n\)个数里选一个\(k\)的因子,使得\(\frac{k}{a_i}\)最小。

点击查看代码
void solve() {
    int n, k;
    std::cin >> n >> k;
    std::vector<int> a(n);
    for (int i = 0; i < n; ++ i) {
    	std::cin >> a[i];
    }

    int ans = k;
    for (int i = 0; i < n; ++ i) {
    	if (k % a[i] == 0) {
    		ans = std::min(ans, k / a[i]);
    	}
    }

    std::cout << ans << "\n";
}

B. Browser

题意:你要把\([1, l - 1]\)\([r + 1, n]\)删除,你在\(pos\),需要移动到\(l\)\(r\)。使得移动距离最小。

分类讨论,如果\(l=1, r=n\),则无需操作。如果\(l=1\)则只要移动到\(r\),如果\(r=n\)只要移动到\(l\)。否则就看从\(l\)再到\(r\)距离小还是从\(r\)\(l\)距离小。

点击查看代码
void solve() {
    int n, p, l, r;
    std::cin >> n >> p >> l >> r;
    if (l == 1 && r == n) {
    	std::cout << 0 << "\n";
    } else if (l == 1) {
    	std::cout << std::abs(r - p) + 1 << "\n";
    } else if (r == n) {
    	std::cout << std::abs(p - l) + 1 << "\n";
    } else {
    	std::cout << std::min(std::abs(p - l) + 1 + r - l + 1, std::abs(r - p) + 1 + r - l + 1) << "\n";
    }
}

C. Permute Digits

题意:给你两个数字\(a, b\),重排\(a\)使得\(a\)小于等于\(b\)且最大。

如果\(b\)的位数比\(a\)多则直接从大到小排序输出。
否则贪心放能放的最大的,然后如果当前放的等于\(b\)的这一位,则可以找一个小于这一位的放当前位置,然后后面的从大到小放,然后继续枚举。否则当前位小于\(b\),则后面直接从大到小放。

点击查看代码
void solve() {
    std::string s, t;
    std::cin >> s >> t;
	std::sort(s.begin(), s.end(), std::greater<char>());
    if (t.size() > s.size()) {
    	std::cout << s << "\n";
    } else {
    	std::string pre, ans;
    	for (auto & c : t) {
    		int n = s.size();
    		int p = -1;
    		for (int i = 0; i < n; ++ i) {
    			if (s[i] <= c) {
    				p = i;
    				break;
    			}
    		}

    		if (p == -1) {
    			break;
    		}

    		int p1 = -1;
    		for (int i = p; i < n; ++ i) {
    			if (s[i] < c) {
    				p1 = i;
    				break;
    			}
    		}

			if (p1 != -1) {
				ans = std::max(ans, pre + s[p1] + s.substr(0, p1) + s.substr(p1 + 1));  
			}  		

    		pre += s[p];
    		if (s[p] < c) {
    			ans = std::max(ans, pre + s.substr(0, p) + s.substr(p + 1));
    			break;
    		}

    		s.erase(s.begin() + p);
    	}

    	if (pre.size() == t.size()) {
    		ans = pre;
    	}

    	std::cout << ans << "\n";
    }
}

D. Almost Acyclic Graph

题意:给你一个有向图,问能不能只删掉一条边使得图变成有向无环图。

考虑拓扑排序,如果我们枚举删去边,然后拓扑排序判有没有环,会超时。转换思路,删去一条边相当于把一个点入度减少1,那么可以枚举减少度数的点。

点击查看代码
void solve() {
    int n, m;
    std::cin >> n >> m;
    std::vector<std::vector<int>> adj(n);
    std::vector<int> in(n);
    for (int i = 0; i < m; ++ i) {
    	int u, v;
    	std::cin >> u >> v;
    	-- u, -- v;

    	++ in[v];
    	adj[u].push_back(v);
    }

	auto t = in;
    for (int i = 0; i < n; ++ i) {
    	-- in[i];
    	std::queue<int> q;
    	for (int u = 0; u < n; ++ u) {
    		if (in[u] == 0) {
    			q.push(u);
    		}
    	}

    	int cnt = 0;
    	while (q.size()) {
    		++ cnt;
    		int u = q.front(); q.pop();
    		for (auto & v : adj[u]) {
    			if ( -- in[v] == 0) {
    				q.push(v);
    			}
    		}
    	}

    	if (cnt == n) {
    		std::cout << "YES\n";
    		return;
    	}

    	in = t;
    }

    std::cout << "NO\n";
}

E. Physical Education Lessons

题意:一开始有\(n\)个1,两种操作,一个是把\([l, r]\)变成\(0\),一个是把\([l, r]\)变成\(1\),每次操作问1有多少。

明显是线段树,如果离散化处理比较麻烦,考虑动态开点。

点击查看代码
#define ls(u) tr[u].lson
#define rs(u) tr[u].rson

const int N = 3e5 + 5;

struct Node {
	int lson, rson;
	int sum;
	int set;
}tr[N * 55];

int idx = 0;

void pushup(int u) {
	tr[u].sum = tr[ls(u)].sum + tr[rs(u)].sum;
}

void pushdownNode(int & u, int len, int set) {
	if (!u) {
		u = ++ idx;
	}
	tr[u].sum = len * (set - 1);
	tr[u].set = set;
}

void pushdown(int u, int l, int r) {
	if (tr[u].set != 0) {
		int mid = l + r >> 1;
		pushdownNode(ls(u), mid - l + 1, tr[u].set);
		pushdownNode(rs(u), r - mid, tr[u].set);
		tr[u].set = 0;
	}
}

void modify(int & u, int l, int r, int L, int R, int v) {
	if (!u) {
		u = ++ idx;
	}

	if (L <= l && r <= R) {
		pushdownNode(u, r - l + 1, v);
		return;
	}

	pushdown(u, l, r);

	int mid = l + r >> 1;
	if (L <= mid) {
		modify(ls(u), l, mid, L, R, v);
	}

	if (R > mid) {
		modify(rs(u), mid + 1, r, L, R, v);
	}

	pushup(u);
}


void solve() {
    int n, q;
    std::cin >> n >> q;
    int root = 0;
    modify(root, 1, n, 1, n, 2);
    while (q -- ) {
    	int l, r, k;
    	std::cin >> l >> r >> k;
    	modify(root, 1, n, l, r, k);
    	std::cout << tr[root].sum << "\n";
    }
}

F. Imbalance Value of a Tree

题意:给你一棵树,每个点有点权,求所有路径的最大值减最小值的和。

考虑拆分来做,也就是看每个点作为最大值出现几次,以及作为最小值出现几次。
考虑把边权变为两个点的最大值,然后按最小生成树来做,加入当前边,想要让这个边是最大值,那么它连接的两个联通块各选一个点就行了。
最小值同样的求法。

点击查看代码
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;
    std::cin >> n;
    std::vector<int> a(n);
    for (int i = 0; i < n; ++ i) {
    	std::cin >> a[i];
    }

    std::vector<std::array<int, 3>> edges(n - 1);
    for (int i = 0; i + 1 < n; ++ i) {
    	int u, v;
    	std::cin >> u >> v;
    	-- u, -- v;
    	edges[i] = {std::max(a[u], a[v]), u, v};
    }

    std::sort(edges.begin(), edges.end());

    i64 ans = 0;
    DSU d(n);
    for (auto & [w, u, v] : edges) {
    	ans += (i64)w * d.size(u) * d.size(v);
    	d.merge(u, v);
    }

    for (auto & [w, u, v] : edges) {
    	w = std::min(a[u], a[v]);
    }

    std::sort(edges.begin(), edges.end(), std::greater<>());
    
    d.init(n);
    for (auto & [w, u, v] : edges) {
    	ans -= (i64)w * d.size(u) * d.size(v);
    	d.merge(u, v);
    }

    std::cout << ans << "\n";
}
posted @ 2025-03-18 05:11  maburb  阅读(19)  评论(0)    收藏  举报