【带权并查集】codeforces 466 A. DZY Loves Sequences
题目
https://codeforces.com/problemset/problem/446/A
题意
第一行,输入一个正整数 \(n(1 \leq n \leq 10^5)\);
第二行,输入一个长度为 \(n\) 的数组 \(a(1 \leq a[i] \leq 10^9)\)。
你最多可以修改数组中的一个数为任意一个整数,问原数组的最长严格递增子数组的长度是多大?
题解
先用带权并查集,维护出原数组的全部严格递增子数组。
使用严格递增子数组的最小下标作为并查集的代表元,并查集的元素个数作为权值,那么代表元便是子数组的左边界,那么左边界加上并查集的权就是右边界(左闭右开区间)。
想要使得两个相邻的严格递增子数组可以拼接在一起,必须满足以下条件:
- 修改前一个严格子数组的最后一个元素或修改后一个严格递增子数组的第一个元素为你指定的整数;
- 修改后拼接两个子数组,拼接后的数组仍满足严格递增的性质。
特殊地,若子数组权值为 \(1\),则需要考虑修改后能否将该子数组前后两个相邻严格递增子数组拼接起来,判定条件便是前一个严格递增子数组的最后一个元素的值比后一个严格递增子数组的第一个元素的值小于等于 \(2\)。
参考代码
#include<bits/stdc++.h>
using namespace std;
constexpr int N = 1e5 + 7;
int n;
int a[N];
int dsu[N];
int sz[N];
int find(int x) {
return x == dsu[x] ? x : dsu[x] = find(dsu[x]);
}
void merge(int x, int y) {
int fx = find(x), fy = find(y);
dsu[fy] = fx;
sz[fx] += sz[fy];
}
int main() {
ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
cin >> n;
for (int i = 1; i <= n; ++ i) {
cin >> a[i];
dsu[i] = i;
sz[i] = 1;
}
for (int i = 2; i <= n; ++ i) if (a[i] > a[i - 1]) merge(i - 1, i);
auto dfs = [&](auto&&dfs, int l, int r, int len) -> int {
int res = len;
if (len == 1 && a[r] - a[l - 1] > 1) res = sz[find(l - 1)] + sz[find(r)] + 1;
else if (sz[r] || sz[find(l - 1)]) res = max(res, len + 1);
if (r <= n) {
int nel = r, ner = nel + sz[find(nel)];
int nelen = ner - nel;
if (len == 1 || nelen == 1 || a[nel] - a[r - 2] > 1 || a[nel + 1] - a[r - 1] > 1) res = max(res, len + nelen);
res = max(res, dfs(dfs, nel, ner, nelen));
}
return res;
};
cout << dfs(dfs, 1, 1 + sz[1], sz[1]) << '\n';
return 0;
}
浙公网安备 33010602011771号