Educational Codeforces Round 36 (Rated for Div. 2)

Educational Codeforces Round 36 (Rated for Div. 2)

A

直接枚举即可

#include <bits/stdc++.h>

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int n, k;

    std::cin >> n >> k;

    int ans = 1e9;

    for (int i = 1; i <= n; i++) {
        int x;
        std::cin >> x;
        if (k % x == 0) {
            ans = std::min(ans, k / x);
        }
    }

    std::cout << ans << '\n';

    return 0;
}

B

分类讨论:
注意 \(l=1,r=n\) 情况

#include <bits/stdc++.h>

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int n, pos, l, r;
    std::cin >> n >> pos >> l >> r;

    if (l == 1 && r == n) {
        std::cout << 0 << '\n';
        return 0;
    }

    if (l == 1) {
        std::cout << std::abs(r - pos) + 1 << '\n';
    } else if (r == n) {
        std::cout << std::abs(l - pos) + 1 << '\n';
    } else {
        std::cout << r - l + std::min(std::abs(l - pos), std::abs(r - pos)) + 2 << '\n';
    }

    return 0;
}

C

首先特判如果 \(a\) 的位数小于 \(b\),显然可以直接把 \(a\) 的数位从大到小排序即可。

否则就是两个位数一样,我们从高位到地位一次贪心,每次尝试填某个数 \(x\),看看填了之后后面所有数都按顺序填,能不能构造出 \(a\leq b\),如果可行那么这一位就贪心填这个数即可。


D

直接枚举断哪条边,复杂度显然是 \(O(n^2(n+m)\) 显然是爆炸的,但我们发现,由于只能删一条边,
那么我们必然得删在某一个环上面。

由于 \(n\leq 500\),所以我们只要随意找一个图中的环,枚举断掉哪条环上的边即可,然后每次检查一次即可,复杂度是 \(O(n(n+m)\) 的。

#include <bits/stdc++.h>


const int N = 510;
std::vector<int> g[N];
int st[N], fa[N];
std::vector<int> cir;
bool vis[N][N];

bool dfs(int u) {
	st[u] = 1;
	for (auto v : g[u]) {
		if (!st[v]) {
			fa[v] = u;	
			if (dfs(v)) return true;
		} else if (st[v] == 1) {
			int x = u;
			while (true) {
				cir.push_back(x);
				if (x == v) break;
				x = fa[x];
			}
			return true;
		}
	}
	st[u] = 2;
	return false;
}

bool DFS(int u) {
	st[u] = 1;
	for (auto v : g[u]) {
		if (vis[u][v]) continue;
		if (!st[v]) {
			if (DFS(v)) return true;
		} else if (st[v] == 1) {
			return true;
		}
	}
	st[u] = 2;
	return false;
}


int main(){
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);
	
	int n, m;

	std::cin >> n >> m;

	while (m--) {
		int u, v;
		std::cin >> u >> v;
		g[u].push_back(v);
	}	

	bool ok = false;
	for (int i = 1; i <= n; i++) {
		if (!st[i] && dfs(i)) {
			ok = true;
			break;
		}
	}



	if (!ok) {
		std::cout << "YES\n";
		return 0;
	}

	//枚举断掉环上的任意一条边

	for (int i = 0; i < cir.size(); i++) {
		vis[cir[(i + 1) % (int)cir.size()]][cir[i]] = true;
		memset(st, 0, sizeof st);
		bool ok = true;
		for (int j = 1; j <= n; j++) {
			if (!st[j] && DFS(j)) {
				ok = false;
				break;
			}
		}
		if (ok) {
			std::cout << "YES\n";
			return 0;
		}
		vis[cir[(i + 1) % (int)cir.size()]][cir[i]] = false;
	}

	std::cout << "NO\n";


	return 0;
}


E

经典的线段树题,我们把询问离散化,注意端点弄成 \(r+1\),这样我们任意两个相邻点构成的区间长度就可以写成 \(v[x] - v[x-1]\) 了,且是不重不漏的。

然后就变成一个区间推平,区间求和的板子题:

#include <bits/stdc++.h>

const int N = 6e5 + 10;
std::vector<int> v;

int sum[4 * N], tag[4 * N], len[4 * N];

void build(int u, int l, int r) {
    tag[u] = -1;
    if (l == r) {
        sum[u] = len[u] = v[l] - v[l - 1];
        return;
    }
    int mid = (l + r) >> 1;
    build(u << 1, l, mid);
    build(u << 1 | 1, mid + 1, r);
    sum[u] = sum[u << 1] + sum[u << 1 | 1];
    len[u] = len[u << 1] + len[u << 1 | 1];
}

void push(int u) {
    if (tag[u] != -1) {
        sum[u << 1] = len[u << 1] * tag[u];
        sum[u << 1 | 1] = len[u << 1 | 1] * tag[u];
        tag[u << 1] = tag[u];
        tag[u << 1 | 1] = tag[u];
        tag[u] = -1;
    }
}

void modify(int u, int l, int r, int ql, int qr, int t) {
    if (l >= ql && r <= qr) {
        sum[u] = len[u] * t;
        tag[u] = t;
        return;
    }
    push(u);
    int mid = (l + r) >> 1;
    if (ql <= mid) modify(u << 1, l, mid, ql, qr, t);
    if (qr > mid) modify(u << 1 | 1, mid + 1, r, ql, qr, t);
    sum[u] = sum[u << 1] + sum[u << 1 | 1];
}



int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int n, q;


    std::cin >> n >> q;

    v.push_back(1), v.push_back(n + 1);

    std::vector<std::array<int, 3>> qry(q);

    for (int i = 0; i < q; i++) {
        auto& [k, l, r] = qry[i];
        std::cin >> l >> r >> k;
        if (k == 1) k = 0;
        else k = 1;
        v.push_back(l), v.push_back(++r);
    }

    std::sort(v.begin(), v.end());

    v.erase(std::unique(v.begin(), v.end()), v.end());

    int m = (int)v.size() - 1;

    build(1, 1, m);


    for (int i = 0; i < q; i++) {
        auto [k, l, r] = qry[i];
        l = std::lower_bound(v.begin(), v.end(), l) - v.begin() + 1;
        r = std::lower_bound(v.begin(), v.end(), r) - v.begin();
        modify(1, 1, m, l, r, k);
        std::cout << sum[1] << '\n';
    }

    return 0;
}

F

经典的 trick:

  • 首先最大值和最小值可以分开求和
  • 对于这类求两两路径的东西,且和权值大小有关,我们可以直接排序+并查集即可。

按点权从小到大,每次拿出点 \(u\) 查询它所有相邻的边,若 \(v\) 也被处理过了,说明 \(v\) 所在集合和 \(u\) 所在集合任意取两个点,它们的路径都会经过这个目前的最大值的点 \(u\),我们只要维护并查集大小,每次合并的时候算贡献即可。

#include <bits/stdc++.h>

typedef long long i64;

int main(){
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);
	
	int n;
	std::cin >> n;

	std::vector<int> a(n + 1);
	std::vector<int> q(n + 1);

	for (int i = 1; i <= n; i++) {
		q[i] = i;
	}

	std::vector<std::vector<int>> g(n + 1);

	std::vector<int> p(n + 1), sz(n + 1, 0);

	for (int i = 1; i <= n; i++) {
		p[i] = i, sz[i] = 1;
	}
	

	std::function<int(int)> find = [&](int x) {
		return x == p[x] ? x : p[x] = find(p[x]);
	};

	for (int i = 1; i <= n; i++) {
		std::cin >> a[i];
	}

	for (int i = 1; i < n; i++) {
		int u, v;
		std::cin >> u >> v;
		g[u].push_back(v);
		g[v].push_back(u);
	}

	std::sort(q.begin() + 1, q.end(), [&](int x, int y) {
		return a[x] < a[y];
	});

	std::vector<bool> vis(n + 1, false);

	i64 ans = 0;

	for (int i = 1; i <= n; i++) {
		int u = q[i];
		vis[u] = true;
		for (auto v : g[u]) {
			if (!vis[find(v)]) continue;
			ans += 1LL * sz[find(v)] * sz[find(u)] * a[u];
			sz[find(u)] += sz[find(v)];
			p[find(v)] = find(u);
		}
	}


	for (int i = 1; i <= n; i++) {
		p[i] = i, sz[i] = 1;
		vis[i] = false;
	}



	for (int i = n; i >= 1; i--) {
		int u = q[i];
		vis[u] = true;
		for (auto v : g[u]) {
			if (!vis[find(v)]) continue;
			ans -= 1LL * sz[find(v)] * sz[find(u)] * a[u];
			sz[find(u)] += sz[find(v)];
			p[find(v)] = find(u);
		}
	}

	std::cout << ans << '\n';


	return 0;
}

posted @ 2024-04-11 22:11  jackle  阅读(2)  评论(0编辑  收藏  举报