比赛链接:
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;
}