UER #1
A. 猜数
题目描述
给定 \(g,l\),满足 \(gl=ab\),且 \(a,b\) 是 \(g\) 的倍数。求 \(a+b\) 的最小/大值。
思路
根据积一定差小和小,最小值为 \(2\sqrt {g\cdot l}\),最大值为 \(g+l\)。
时空复杂度均为 \(O(1)\)。
代码
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
int t;
ll a, b;
void Solve() {
cin >> a >> b;
cout << 2ll * a * (ll)sqrt(b / a) << " " << a + b << "\n";
}
int main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
for(cin >> t; t--; Solve()) {
}
return 0;
}
C. DZY Loves Graph
题目描述
有 \(N\) 个点,\(M\) 次操作。对于第 \(i\) 次操作有:
- 在 \(a,b\) 之间建一条边权为 \(i\) 的边。
- 撤销最新建的 \(k\) 条边。
- 撤销 \(i-1\) 次操作,保证 \(i-1\) 次操作不为此操作。
请在每次操作后求出最小生成树的边权之和。
思路
我们可以用栈记录当前仍然有效的 1 操作和当时的生成树边权之和和生成树的边的数量。但为了避免这种情况:
Add
Add
...
Delete
Return
Delete
Return
...
我们要记录一个 \(k\),表示当前有 \(k\) 个未处理的撤销。在有新的 Add
操作之前,我们可以直接访问之前的答案。如果有 Add
,那么就一个一个处理撤销。因为此时已经不可能再去 Return
之前的 Delete
了。使用可撤销化并查集。
空间复杂度 \(O(N+M)\),时间复杂度 \(O(M\log N)\)。
代码
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<int, int>;
const int MAXM = 500001, MAXN = 300001;
struct query {
string op;
int a, b;
}a[MAXM];
int n, m, f[MAXN], sz[MAXN], k, cnt[MAXM], stk[MAXN], top;
ll sum[MAXM];
vector<pii> edge;
int getfa(int u) {
return (f[u] == u ? u : getfa(f[u]));
}
bool Merge(int u, int v) {
u = getfa(u), v = getfa(v);
if(u != v) {
if(sz[u] > sz[v]) {
swap(u, v);
}
f[u] = v, sz[v] += sz[u];
edge.emplace_back(u, v);
return 1;
}else {
edge.emplace_back(0, 0);
return 0;
}
}
void Cancal() {
auto [u, v] = edge.back();
edge.pop_back();
f[u] = u, sz[v] -= sz[u];
}
int main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n >> m;
iota(f + 1, f + n + 1, 1);
fill(sz + 1, sz + n + 1, 1);
cnt[0] = n, sum[0] = 0;
for(int i = 1; i <= m; ++i) {
cin >> a[i].op;
if(a[i].op == "Add") {
cin >> a[i].a >> a[i].b;
for(; k; k--, top--) {
Cancal();
}
cnt[i] = cnt[stk[top]], sum[i] = sum[stk[top]];
if(Merge(a[i].a, a[i].b)) {
cnt[i]--;
sum[i] += i;
}
stk[++top] = i;
cout << (cnt[i] != 1 ? 0 : sum[i]) << "\n";
}else if(a[i].op == "Delete") {
cin >> a[i].a;
k += a[i].a;
cout << (cnt[stk[top - k]] != 1 ? 0 : sum[stk[top - k]]) << "\n";
}else {
if(a[i - 1].op == "Add") {
k++;
cout << (cnt[stk[top - k]] != 1 ? 0 : sum[stk[top - k]]) << "\n";
}else {
k -= a[i - 1].a;
cout << (cnt[stk[top - k]] != 1 ? 0 : sum[stk[top - k]]) << "\n";
}
}
}
return 0;
}