Description
给定长度为 n 的序列 p
找出尽可能多的三元组 (ai,bi,ci) 满足:
- 1≤ai<bi<ci≤n
- pai=pci=0,pbi=0
- pbi 互不相同。
- 所有的 ai,bi,ci 互不相同。
输出最多可以选出多少个三元组,多组数据。
∑n≤5⋅105
Solution
设总共有 c 个 0,容易发现答案的上界是 ⌊2c⌋,并且对于每个三元组,ai 一定在前 ⌊2c⌋ 个,ci 一定在最后 ⌊2c⌋ 个。
因为如果不满足那么调整成这个情况一定更优。
然后考虑一个 bi,如果在前 ⌊2c⌋ 个 0 的区间里,那么如果它能够与 ⌊2c⌋ 匹配那么一定能和右边的 0 匹配,如果在右边则一定能与左边的 0 匹配。
所以只要把 mid 左边和右边的分开考虑然后把答案相加与 ⌊2c⌋ 取 min。
容易发现对于一个颜色,只有 <mid 的最大位置和 >mid 的最小位置有意义,所以把这个颜色与 <mid 的位置之前的 0 和 >mid 的位置之后的 0 连边跑网络流即可,显然过不了。
注意到一个颜色匹配的一定是一个前缀+一个后缀,所以把 mid 之前的 0 移到最后面,那么每个颜色匹配的就是一个区间 [l,r],于是只要先按 r 排序然后贪心即可。
时间复杂度:O(nlogn)。
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| #include <bits/stdc++.h>
const int kMaxN = 5e5 + 5; int n; int a[kMaxN]; std::vector<int> vec[kMaxN]; void dickdreamer() { std::cin >> n; for (int i = 0; i <= n; ++i) vec[i].clear(); for (int i = 1; i <= n; ++i) { std::cin >> a[i]; vec[a[i]].emplace_back(i); } if (!vec[0].size()) return void(std::cout << "0\n"); int cnt = vec[0].size(), L, R; if (vec[0].size() & 1) L = R = vec[0][(vec[0].size() - 1) / 2]; else L = vec[0][vec[0].size() / 2 - 1], R = vec[0][vec[0].size() / 2]; std::vector<std::pair<int, int>> rg; for (int i = 1; i <= n; ++i) { if (!vec[i].size()) continue; int id1 = -1, id2 = n + 1; for (auto x : vec[i]) { if (x < R) id1 = x; if (x > L && id2 == n + 1) id2 = x; } id1 = std::lower_bound(vec[0].begin(), vec[0].end(), id1) - vec[0].begin() - 1; id2 = std::lower_bound(vec[0].begin(), vec[0].end(), id2) - vec[0].begin(); rg.emplace_back(id2, cnt + id1); } auto cmp = [](const std::pair<int, int> &p1, const std::pair<int, int> &p2) { return std::make_pair(p1.second, p1.first) < std::make_pair(p2.second, p2.first); }; std::sort(rg.begin(), rg.end(), cmp); std::set<int> st; for (int i = 0; i < 2 * cnt; ++i) st.emplace(i); int ans = 0; for (auto p : rg) { auto it = st.lower_bound(p.first); if (it != st.end() && *it <= p.second) ++ans, st.erase(it); } std::cout << std::min(ans, cnt / 2) << '\n'; } int32_t main() { #ifdef ORZXKR freopen("in.txt", "r", stdin); freopen("out.txt", "w", stdout); #endif std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0); int T = 1; std::cin >> T; while (T--) dickdreamer(); return 0; }
|