C. 造桥与砍树
魔改 prim。
回顾经典的prim算法,从第一个点开始,每次找权值最小的边加入,然后更新到每个点的距离和边的起点,以此往复。
对于这道题来讲,每次更新到其他 \(n-1\) 个点的最小权值显然复杂度太大,但按照贪心的来讲,我们其实每次只选最小的那条边加入而已,所以对于点 \(u\) 来说每次二分去找到权值最接近 \(k-t_u\) 的点即可,如果没有,那就选点权最小的加进去。
按照 prim 算法,维护一条边的两个点,如果我们准备要加入的这个点和我们之前已经连边的点是一个集合,那就不用再加这个点了,重新选一条边加入堆维护即可;否则加入这个点,再对两个点都选取一条边加入堆维护。
点击查看代码
#include <bits/stdc++.h>using namespace std;using i64 = long long;void solve() {int n, k;cin >> n >> k;set<array<int,2>> s;vector<int> a(n + 1);for (int i = 1; i <= n; i += 1) {cin >> a[i];a[i] %= k;s.insert({a[i], i});}auto get = [&](int x)->array<int,2> {auto t = s.lower_bound({k - x, 0});return t == s.end() ? *s.begin() : *t;};priority_queue<array<int,3>,vector<array<int,3>>,greater<>> Q;auto [w1, st] = *s.begin();s.erase(s.begin());auto [w2, nxt] = get(w1);i64 ans = 0;Q.push({(w1 + w2) % k, st, nxt});while (Q.size()) {auto [d, u, v] = Q.top();Q.pop();if (!s.count({a[v], v})) {auto [w, x] = get(a[u]);Q.push({(w + a[u]) % k, u, x});continue;}ans += d;s.erase(s.lower_bound({a[v], v}));if (s.empty()) {break;}auto [w1, x] = get(a[v]);Q.push({(w1 + a[v]) % k, v, x});auto [w2, y] = get(a[u]);Q.push({(w2 + a[u]) % k, u, y});}cout << ans << "\n";}
int main() {ios::sync_with_stdio(false);cin.tie(nullptr);int t;cin >> t;while (t--) {solve();}return 0;
}
换了 Boruvka 算法,听说有人过了,但是我的被卡了(悲qwq
T 掉的代码:
点击查看代码
#include <bits/stdc++.h>using namespace std;using i64 = long long;struct DSU {std::vector<int> f, siz;DSU() {}DSU(int n) {init(n);}void init(int n) {f.resize(n);std::iota(f.begin(), f.end(), 0);siz.assign(n, 1);}int find(int x) {while (x != f[x]) {x = f[x] = f[f[x]];}return x;}bool same(int x, int y) {return find(x) == find(y);}bool merge(int x, int y) {x = find(x);y = find(y);if (x == y) {return false;}siz[x] += siz[y];f[y] = x;return true;}int size(int x) {return siz[find(x)];}
};void solve() {int n, k;cin >> n >> k;multiset<array<int,2>> s;vector<int> a(n + 1);vector<vector<int>> num(n + 1);for (int i = 1; i <= n; i += 1) {cin >> a[i];a[i] %= k;s.insert({a[i], i});num[i].push_back(i);}auto get = [&](int x)->array<int,2> {auto t = s.lower_bound({k - x, 0});return t == s.end() ? *s.begin() : *t;};i64 ans = 0;DSU dsu(n + 1);while (true) {for (int i = 1; i <= n; i += 1) {if (dsu.find(i) != i) {continue;}for (auto x : num[i]) {s.extract({a[x], x});}int Min = INT_MAX, nxt = -1;for (auto x : num[i]) {auto [w, y] = get(a[x]);w = (w + a[x]) % k;if (Min > w) {Min = w, nxt = y;if (Min == 0) {break;}}}for (auto x : num[i]) {s.insert({a[x], x});}if (nxt != -1) {auto &ve = num[dsu.find(nxt)];num[i].insert(num[i].end(), ve.begin(), ve.end());dsu.merge(i, nxt);ans += Min;}}if (dsu.size(1) == n) {break;}}cout << ans << "\n";}
int main() {ios::sync_with_stdio(false);cin.tie(nullptr);int t;cin >> t;while (t--) {solve();}return 0;
}
G. 序列与整数对
根号分治。
对出现次数小于 \(B\) 的暴力枚举,然后二分另一个数的贡献统计即可,取 \(B = \sqrt{\frac{n^2}q}\) 时,复杂度为 \(O(q\sqrt{n}\log n)\)。
直接暴力枚举也行,按哪个出现次数少就枚举哪个,然后记忆化,这样下来的复杂度貌似均摊也和上面一样。
点击查看代码
#include <bits/stdc++.h>using namespace std;using i64 = long long;int main() {ios::sync_with_stdio(false);cin.tie(nullptr);int n, q;cin >> n >> q;vector<int> a(n + 1);map<int, vector<int>> mp;for (int i = 1; i <= n; i += 1) {cin >> a[i];mp[a[i]].push_back(i);}int B = sqrt(n / q * n);map<array<int,2>,i64> ans;while (q --) {int x, y;cin >> x >> y;if (ans.count({x, y})) {cout << ans[ {x, y}] << "\n";continue;}i64 res = 0;int m = mp[x].size(), k = mp[y].size();if (x == y) {cout << 1LL * (m - 1) * m / 2 << "\n";continue;}else if (m < B || m >= B && k >= B && m < k) {for (auto &u : mp[x]) {res += mp[y].end() - lower_bound(mp[y].begin(), mp[y].end(), u);}}else {for (auto &v : mp[y]) {res += lower_bound(mp[x].begin(), mp[x].end(), v) - mp[x].begin();}}ans[ {x, y}] = res;cout << res << "\n";}return 0;
}
离线离散化,预处理大于 \(\sqrt n\) 的部分,对于两个都小于 \(\sqrt n\) 的部分,可以直接双指针处理,这部分最多也是 \(\sqrt n\) 次,所以复杂度为 \(O(n \sqrt n + q(\log n +\sqrt n))\)。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
const int N=1e5+3;
#define int long long
int n,q;
void solve() {cin >> n >> q;int K = sqrt(n);vector<int> a(n + 1);vector<int> p;for (int i = 1; i <= n; ++i) {cin >> a[i];p.push_back(a[i]);}vector<array<int, 2>> Q;for (int i = 0; i < q; ++i) {int x, y;cin >> x >> y;Q.push_back({x, y});p.push_back(x);p.push_back(y);}std::sort(p.begin(), p.end());p.erase(unique(p.begin(), p.end()), p.end());int m=p.size();vector<int> cnt(m + 3);vector<int> mp[m +3];for (int i = 1; i <= n; ++i) {a[i] = lower_bound(p.begin(), p.end(), a[i]) - p.begin() + 1;mp[a[i]].push_back(i);cnt[a[i]]++;}vector<int> g1[m+3];vector<int> g2[m+3];for (int i = 0; i < q; ++i) {Q[i][0] = lower_bound(p.begin(), p.end(), Q[i][0]) - p.begin() + 1;Q[i][1] = lower_bound(p.begin(), p.end(), Q[i][1]) - p.begin() + 1;int x = Q[i][0];int y = Q[i][1];g1[x].push_back(y);g2[y].push_back(x);}vector<int> ans1[m+3];vector<int> ans2[m+3];for (int i = 1; i <= m; ++i) {std::sort(g1[i].begin(), g1[i].end());g1[i].erase(unique(g1[i].begin(), g1[i].end()), g1[i].end());ans1[i].resize(g1[i].size());std::sort(g2[i].begin(), g2[i].end());g2[i].erase(unique(g2[i].begin(), g2[i].end()), g2[i].end());ans2[i].resize(g2[i].size());}vector<int> idx(m + 3, -1);for (int i = 1; i <= m; ++i) {if (g1[i].empty())continue;if (cnt[i] >= K) {int p = 0;for (auto x: g1[i]) {idx[x] = p++;}int sum = 0;for (int j = 1; j <= n; ++j) {int y = a[j];if (idx[y] != -1) {if (cnt[y] < K) {ans1[i][idx[y]] += sum;}}if (y == i)sum++;}for (auto x: g1[i]) {idx[x] = -1;}}}for (int i = 1; i <= m; ++i) {if (g2[i].empty())continue;if (cnt[i] >= K) {int p = 0;for (auto x: g2[i]) {idx[x] = p++;}int sum = 0;for (int j = n; j >= 1; --j) {int y = a[j];if (idx[y] != -1) {ans2[i][idx[y]] += sum;}if (y == i)sum++;}for (auto x: g2[i]) {idx[x] = -1;}}}for (auto [x, y]: Q) {if (cnt[x] >= K and cnt[y] < K) {int id= lower_bound(g1[x].begin(), g1[x].end(),y)-g1[x].begin();cout<<ans1[x][id]<<endl;} else if (cnt[y] >= K) {int id= lower_bound(g2[y].begin(), g2[y].end(),x)-g2[y].begin();cout<<ans2[y][id]<<endl;} else {
// cout<<x<<' '<<y<<endl;int sum = 0;int l = 0;for (int i = 0; i < mp[y].size(); ++i) {auto id = mp[y][i];while (l < mp[x].size() and mp[x][l] < id) {l++;}sum += l;
// cout<<sum<<' ';}
// cout<<endl;cout<<sum<<endl;}}
}signed main(){ios::sync_with_stdio(false),cin.tie(0);int t=1;
// cin>>t;while (t--){solve();}
}