比赛链接:

https://vjudge.net/contest/508640

F - Maex

题意:

对有 \(n\) 个节点的树进行赋值,\(a_u\) 在 0 到 \(n - 1\) 的范围中且每个节点的 \(a_u\) 不同,\(b_u\)\(u\) 及它的子树的 \(a_v\)\(MEX\)

思路:

赋值之后是没有后效性的,考虑树形 \(dp\)。每个节点的子树大小其实就是它的 \(b_u\) 的最大值,容易得到转移方程 \(dp[u] = max(dp[u], dp[v] + sz[u])\)

代码:

#include <bits/stdc++.h>
using namespace std;
#define LL long long
void solve(){
	int n;
	cin >> n;
	vector < vector<int> > e(n + 1);
	for (int i = 0; i < n - 1; i ++ ){
		int u, v;
		cin >> u >> v;
		e[u].push_back(v);
		e[v].push_back(u);
	}
	vector <LL> sz(n + 1), dp(n + 1);
	function<void(int, int)> dfs = [&](int u, int fa){
		sz[u] = 1;
		for (auto v : e[u]){
			if (v == fa) continue;
			dfs(v, u);
			sz[u] += sz[v];
			dp[u] = max(dp[u], dp[v]);
		}
		dp[u] += sz[u];
	};
	dfs(1, 0);
	cout << dp[1] << "\n";
}
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	int T = 1;
	cin >> T;
	while(T -- ){
		solve();
	}
	return 0;
}

J - Planar graph

题意:

在一张 \(n\) 个点构成的平面图中,有 \(m\) 条边,将整个区域划分成若干块,问破掉哪些边上才能使各个区域连通。若有多组方案,输出字典序最小的。

思路:

转化一下,就是求 \(n\) 个点的最大生成树,因为树的结构中只有 \(n - 1\) 条边,也就意味着所有区域都是连通的,同时字典需要最小,那么删除的边的序号要尽可能小,所以是找最大生成树。
按照编号从大到小枚举边,通过并查集去判断两个点是否连通,最后输出删掉的边即可。

代码:

#include <bits/stdc++.h>
using namespace std;
#define LL int
struct dsu{
	LL n;
	vector <LL> p;
	dsu(LL n) : n(n){
		p.resize(n + 1);
		iota(p.begin(), p.end(), 0);
	}
	LL get(LL x){
		return (x == p[x] ? x : (p[x] = get(p[x])));
	}
	void unite(LL x, LL y){
		x = get(x);
		y = get(y);
		if (x != y){
			p[x] = y;
		}
	}
};
void solve(){
	int n, m;
	cin >> n >> m;
	dsu d(n);
	vector < pair<int, int> > e(m + 1);
	for (int i = 1; i <= m; i ++ )
		cin >> e[i].first >> e[i].second;
	vector <int> ans;
	for (int i = m; i >= 1; i -- ){
		if (d.get(e[i].first) == d.get(e[i].second)){
			ans.push_back(i);
		}
		else{
			d.unite(e[i].first, e[i].second);
		}
	}
	reverse(ans.begin(), ans.end());
	cout << ans.size() << "\n";
	for (auto x : ans)
		cout << x << " ";
	cout << "\n";
}
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	int T = 1;
	cin >> T;
	while(T -- ){
		solve();
	}
	return 0;
}

L - Loop

题意:

长为 \(n\) 的序列,每次可以选择 \([L, R]\) 这个区间,将第 \(L\) 位放到最后面,后面的元素依次向前移动一位,这个操作可以进行 \(k\) 次,问序列的最大字典序会是多少。

思路:

当出现 \(a_i < a_{i + 1}\) 的时候,就要将 \(a_i\) 放到后面的某个位置,用单调栈去实现这个查找的过程,最多弹出 \(k\) 个元素,这些元素可以放在后面的任意位置,按照大到小放就行。

代码:

#include <bits/stdc++.h>
using namespace std;
#define LL long long
void solve(){
	int n, k;
	cin >> n >> k;
	vector <int> a(n);
	for (int i = 0; i < n; i ++ )
		cin >> a[i];
	vector <int> b;
	priority_queue <int, vector<int>, less<int> > q;
	for (int i = 0; i < n; i ++ ){
		while(b.size() && b.back() < a[i] && k){
			q.push(b.back());
			b.pop_back();
			k -- ;
		}
		b.push_back(a[i]);
	}
	vector <int> ans;
	int p = 0;
	b.push_back(-1e9);
	q.push(-1e9);
	while(ans.size() < n){
		if (q.top() > b[p]){
			ans.push_back(q.top());
			q.pop();
		}
		else{
			ans.push_back(b[p]);
			p ++ ;
		}
	}
	for (int i = 0; i < n; i ++ )
		cout << ans[i] << " \n"[i == n - 1];
}
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	int T = 1;
	cin >> T;
	while(T -- ){
		solve();
	}
	return 0;
}
posted on 2022-08-14 15:35  Hamine  阅读(34)  评论(0编辑  收藏  举报