2021杭电多校8
2021“MINIEYE杯”中国大学生算法设计超级联赛(8)
A
B
C
\(n=5000\) 的完全图求最小生成树的最长边长度。堆优化的 Prim 复杂度 \(O(m\log n)\),Kruskal 复杂度 \(O(m\log m)\),都过不去。只有朴素的 Prim 算法 \(O(n^2)\) 可以过去。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 5e3 + 5;
using ll = long long;
const ll inf = 0x3f3f3f3f3f3f3f3f;
int x[maxn], y[maxn];
namespace Prim {
ll g[maxn][maxn], dis[maxn];
int vis[maxn];
ll naivePrim(int n) {
memset(dis, 0x3f, sizeof(ll) * (n + 1));
memset(vis, 0, sizeof(int) * (n + 1));
int cnt = 1, u = 1;
ll res = 0;
vis[1] = 1;
while (cnt < n) {
ll now = inf, v = 0;
for (int i = 1; i <= n; ++i) {
if (vis[i])
continue;
ll tmp = g[u][i];
if (tmp < dis[i]) {
dis[i] = tmp;
vis[i] = 0;
}
if (dis[i] < now)
now = dis[v = i];
}
res = max(res, now);
vis[u = v] = 1;
cnt++;
}
return res;
}
}; // namespace Prim
using namespace Prim;
void solve() {
int n;
cin >> n;
for (int i = 1; i <= n; ++i)
cin >> x[i] >> y[i];
function<ll(int, int)> calc = [&](int a, int b) -> ll {
ll dx = x[a] - x[b], dy = y[a] - y[b];
return (dx * dx + dy * dy);
};
for (int i = 1; i < n; ++i) {
for (int j = i + 1; j <= n; ++j) {
g[i][j] = g[j][i] = calc(i, j);
}
}
cout << naivePrim(n) << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
int T = 1;
cin >> T;
while (T--)
solve();
return 0;
}
D
对序列进行三种操作:
- \([l,r]\) 内每个数减去 \(\text{lowbit}\)
- \([l,r]\) 内每个数加上最高位
- 求 \([l,r]\) 区间和
容易想到把每个数拆成最高位和剩下的部分,用两棵线段树分别维护。此时操作 1 就可以暴力修改叶子,如果减到 \(0\) 了就把高位线段树的相应位置变成 \(0\);操作 2 用一棵区间乘 + 区间赋值 + 区间和线段树就可以维护。这样每次操作线段树的均摊复杂度应该是 \(\Theta(\log^2 n)\)。求和的时候用快速幂也能过,如果事先预处理出 \(2\) 的幂会更快一些(967ms -> 826ms)。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5;
using ll = long long;
const ll inf = 0x3f3f3f3f3f3f3f3f;
const ll mod = 998244353;
ll lowbit(ll x) { return x & (-x); }
ll pow2[maxn << 1];
namespace sgt_high {
int tag[maxn << 2];
bool flag[maxn << 2];
ll sum[maxn << 2];
void push_up(int p) {
sum[p] = (sum[p << 1] + sum[p << 1 | 1]) % mod;
flag[p] = (flag[p << 1] & flag[p << 1 | 1]);
}
void build(int p, int l, int r, int *a) {
sum[p] = 0, flag[p] = 0, tag[p] = 0;
if (l == r) {
sum[p] = a[l];
return;
}
int mid = (l + r) >> 1;
build(p << 1, l, mid, a);
build(p << 1 | 1, mid + 1, r, a);
push_up(p);
}
void push_down(int p) {
tag[p << 1] += tag[p];
tag[p << 1 | 1] += tag[p];
sum[p << 1] = sum[p << 1] * pow2[tag[p]] % mod;
sum[p << 1 | 1] = sum[p << 1 | 1] * pow2[tag[p]] % mod;
tag[p] = 0;
}
void assign(int p, int l, int r, int x, int y) {
if (flag[p])
return;
if (x <= l && r <= y) {
flag[p] = 1;
sum[p] = 0;
return;
}
if (tag[p])
push_down(p);
int mid = (l + r) >> 1;
if (x <= mid)
assign(p << 1, l, mid, x, y);
if (y > mid)
assign(p << 1 | 1, mid + 1, r, x, y);
push_up(p);
}
void modify(int p, int l, int r, int x, int y) {
if (flag[p]) {
return;
}
if (x <= l && r <= y) {
sum[p] = sum[p] * 2 % mod;
tag[p]++;
return;
}
if (tag[p])
push_down(p);
int mid = (l + r) >> 1;
if (x <= mid)
modify(p << 1, l, mid, x, y);
if (y > mid)
modify(p << 1 | 1, mid + 1, r, x, y);
push_up(p);
}
ll query(int p, int l, int r, int x, int y) {
if (flag[p])
return 0;
if (x <= l && r <= y) {
return sum[p] % mod;
}
ll ret = 0;
int mid = (l + r) >> 1;
if (tag[p])
push_down(p);
if (x <= mid)
ret = (ret + query(p << 1, l, mid, x, y)) % mod;
if (y > mid)
ret = (ret + query(p << 1 | 1, mid + 1, r, x, y)) % mod;
return ret;
}
}; // namespace sgt_high
namespace sgt_low {
int tag[maxn << 2]; // tag : subtree[p] -= lowbit[p]
bool flag[maxn << 2]; // subtree[p] = 0 ? 1 : 0
ll sum[maxn << 2];
void push_up(int p) {
sum[p] = (sum[p << 1] + sum[p << 1 | 1]) % mod;
flag[p] = (flag[p << 1] & flag[p << 1 | 1]);
}
void build(int p, int l, int r, int *a) {
sum[p] = 0, flag[p] = 0, tag[p] = 0;
if (l == r) {
sum[p] = a[l];
if (sum[p] == 0)
flag[p] = 1;
return;
}
int mid = (l + r) >> 1;
build(p << 1, l, mid, a);
build(p << 1 | 1, mid + 1, r, a);
push_up(p);
}
void modify(int p, int l, int r, int x, int y, int n) {
if (x <= l && r <= y && flag[p]) {
sgt_high::assign(1, 1, n, l, r);
return;
}
if (l == r) {
sum[p] -= lowbit(sum[p]);
if (sum[p] == 0)
flag[p] = 1;
return;
}
int mid = (l + r) >> 1;
if (x <= mid)
modify(p << 1, l, mid, x, y, n);
if (y > mid)
modify(p << 1 | 1, mid + 1, r, x, y, n);
push_up(p);
}
ll query(int p, int l, int r, int x, int y) {
if (x <= l && r <= y) {
return sum[p] % mod;
}
ll ret = 0;
int mid = (l + r) >> 1;
if (x <= mid)
ret = (ret + query(p << 1, l, mid, x, y)) % mod;
if (y > mid)
ret = (ret + query(p << 1 | 1, mid + 1, r, x, y)) % mod;
return ret;
}
}; // namespace sgt_low
int a[maxn], b[maxn];
void solve() {
int n, q;
cin >> n;
for (int i = 1; i <= n; ++i) {
cin >> a[i];
}
for (int i = 1, x; i <= n; ++i) {
x = 31 - __builtin_clz(a[i]);
a[i] -= (1 << x), b[i] = (1 << x);
}
sgt_high::build(1, 1, n, b);
sgt_low::build(1, 1, n, a);
cin >> q;
for (int i = 1, op, l, r; i <= q; ++i) {
cin >> op >> l >> r;
if (op == 1) {
ll res = sgt_low::query(1, 1, n, l, r);
res += sgt_high::query(1, 1, n, l, r);
cout << res % mod << '\n';
} else if (op == 2) {
sgt_low::modify(1, 1, n, l, r, n);
} else {
sgt_high::modify(1, 1, n, l, r);
}
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
pow2[0] = 1;
for (int i = 1; i <= 200000; ++i) {
pow2[i] = pow2[i - 1] << 1;
if (pow2[i] >= mod)
pow2[i] -= mod;
}
int T = 1;
cin >> T;
while (T--)
solve();
return 0;
}
E
F
队友写的,好像可以转化成 nim 的结论,还要用线性筛搞一下。
G
H
队友写的计算几何。
I
队友写的哈希。

浙公网安备 33010602011771号