VP Educational Codeforces Round 78 (Rated for Div. 2)
A. Shuffle Hashing
题意:判断\(s\)排序后是不是和\(t\)的一段子串排序相等。
点击查看代码
void solve() {
std::string s, t;
std::cin >> s >> t;
int n = s.size(), m = t.size();
std::ranges::sort(s);
for (int i = 0; i + n - 1 < m; ++ i) {
std::string ss = t.substr(i, n);
std::ranges::sort(ss);
if (ss == s) {
std::cout << "YES\n";
return;
}
}
std::cout << "NO\n";
}
B. A and B
题意:两个数\(a, b\)。第\(i\)次操作使得其中一个数加\(i\)。求几次操作使得\(a, b\)相等。
猜测是\(\frac{n(n+1)}{2}+|a-b|\)是偶数且\(\frac{n(n+1)}{2}\geq |a-b|\)时成立。
点击查看代码
void solve() {
int a, b;
std::cin >> a >> b;
int c = std::abs(a - b);
int ans = 0;
i64 sum = 0;
while (sum < c || (sum - c) % 2) {
++ ans;
sum += ans;
}
std::cout << ans << "\n";
}
C. Berry Jam
题意:\(2n\)个点,有些是\(1\),有些是\(2\)你需要从\(n\)和\(n+1\)开始,每次删去相邻的一个点。求使得\(1, 2\)个数相同的最少操作数。
设\(1\)是加一,\(2\)是减一。预处理一个向前得到\(d\)的贡献的最小距离。然后向后枚举值\(d\),与之前的\(-d\)的贡献相加去最小值。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::vector<int> a(2 * n);
for (int i = 0; i < 2 * n; ++ i) {
std::cin >> a[i];
}
int cnt1 = std::ranges::count(a, 1);
int cnt2 = std::ranges::count(a, 2);
std::map<int, int> pre;
int d = 0;
pre[0] = 0;
for (int i = n - 1; i >= 0; -- i) {
if (a[i] == 1) {
-- d;
} else {
++ d;
}
if (!pre.count(d)) {
pre[d] = n - i;
}
}
d = cnt1 - cnt2;
int ans = pre.count(-d) ? pre[-d] : 2 * n;
for (int i = n; i < 2 * n; ++ i) {
if (a[i] == 1) {
-- d;
} else {
++ d;
}
if (pre.count(-d)) {
ans = std::min(ans, i - n + 1 + pre[-d]);
}
}
std::cout << ans << "\n";
}
D. Segment Tree
题意:给你\(n\)个线段,如果两个线段相交但不包含就会连边。求能不能构成一棵树。
先按左端点排序,那么对于\(i\)来说前面和它相交的线段只需要满足右端点在\([l_i, r_i]\)之间。那么用树状数组维护区间和就可以得到边数。
如果边数不为\(n-1\)则不是。如果等于\(n-1\),再把所有边取出来看是不是一个联通块。因为已经确定只有\(n-1\)条边,可以用\(set\)暴力找。
点击查看代码
template <class T>
struct Fenwick {
int n;
std::vector<T> tr;
Fenwick(int _n) {
init(_n);
}
void init(int _n) {
n = _n;
tr.assign(_n + 1, T{});
}
void add(int x, const T &v) {
for (int i = x; i <= n; i += i & -i) {
tr[i] = tr[i] + v;
}
}
T query(int x) {
T res{};
for (int i = x; i; i -= i & -i) {
res = res + tr[i];
}
return res;
}
T sum(int l, int r) {
return query(r) - query(l - 1);
}
};
struct DSU {
std::vector<int> fa, cnt;
DSU(int _n) {
init(_n);
}
void init(int _n) {
fa.assign(_n, 0);
cnt.assign(_n, 1);
std::iota(fa.begin(), fa.end(), 0);
}
int find(int x) {
return x == fa[x] ? x : fa[x] = find(fa[x]);
}
bool merge(int x, int y) {
x = find(x), y = find(y);
if (x == y) {
return false;
}
fa[y] = x;
cnt[x] += cnt[y];
return true;
}
bool same(int x, int y) {
return find(x) == find(y);
}
int size(int x) {
return cnt[find(x)];
}
};
void solve() {
int n;
std::cin >> n;
std::vector<std::array<int, 3>> a(n);
for (int i = 0; i < n; ++ i) {
int l, r;
std::cin >> l >> r;
a[i] = {l, r, i};
}
i64 cnt = 0;
std::ranges::sort(a);
Fenwick<int> tr(2 * n);
for (auto & [l, r, id] : a) {
int v = tr.sum(l, r);
cnt += v;
tr.add(r, 1);
}
if (cnt == n - 1) {
DSU dsu(n);
std::set<std::pair<int, int>> s;
for (auto & [l, r, id] : a) {
auto it = s.lower_bound({l, 0}), ij = s.upper_bound({r, 0});
while (it != ij) {
dsu.merge(id, it->second);
++ it;
}
s.emplace(r, id);
}
for (int i = 1; i < n; ++ i) {
if (!dsu.same(0, i)) {
std::cout << "NO\n";
return;
}
}
std::cout << "YES\n";
} else {
std::cout << "NO\n";
}
}
E. Tests for problem D
题意:和\(D\)相反。给你树,要求构造一个满足条件的\(n\)个区间。端点在\([1, 2n]\)之间,且互不相同。
\(n\)个点正好用完\([1, 2n]\)之间的点,所以猜测对于子树大小为\(size_i\)的点,它的区间应该为\([l_i, l_i + size_i + 1]\),且它的子节点的左端点都在这个区间里。为了人子节点的区间不相交,相离比较困难,可以使得它们互相包含,也就是人左端点小的子节点右端点尽可能大。那么我们可以\(dfs\)遍历,每次确定左端点和右端点,然后从左端点大的子节点遍历,得到它的右端点,那么下一个子节点的右端点要大于这个右端点,同理为了节省空间,应该是紧挨着的。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::vector<std::vector<int>> adj(n);
for (int i = 1; i < n; ++ i) {
int u, v;
std::cin >> u >> v;
-- u, -- v;
adj[u].push_back(v);
adj[v].push_back(u);
}
std::vector<int> l(n), r(n);
int idx = 0;
auto dfs = [&](auto & self, int u, int R, int fa) -> int {
r[u] = R + adj[u].size();
int idx = r[u] - 1;
R = r[u];
for (auto & v : adj[u]) {
if (v == fa) {
continue;
}
l[v] = idx -- ;
R = self(self, v, R, u);
}
return R;
};
l[0] = 1;
dfs(dfs, 0, 2, -1);
for (int i = 0; i < n; ++ i) {
std::cout << l[i] << " " << r[i] << "\n";
}
}

浙公网安备 33010602011771号