Japan Registry Services (JPRS) Programming Contest 2025#1 (AtCoder Beginner Contest 392)


A - Shuffled Equation

点击查看代码
void solve() {
    int a, b, c;
    std::cin >> a >> b >> c;
    if (a * b == c || a * c == b || b * c == a) {
    	std::cout << "Yes\n";
    } else {
    	std::cout << "No\n";
    }
}

B - Who is Missing?

点击查看代码
void solve() {
    int n, m;
    std::cin >> n >> m;
    std::set<int> s;
    for (int i = 0; i < m; ++ i) {
    	int x;
    	std::cin >> x;
    	s.insert(x);
    }

    std::vector<int> ans;
    for (int i = 1; i <= n; ++ i) {
    	if (!s.count(i)) {
    		ans.push_back(i);
    	}
    }

    std::cout << ans.size() << "\n";
    for (auto & x : ans) {
    	std::cout << x << " \n"[x == ans.back()];
    }
}

C - Bib

题意:第\(i\)个人编号为\(q_i\),指向\(p_i\)。求编号为\(i\)的人指向的人的编号。

编号为\(q_i\)的人指向人的编号为\(q_{p_i}\)

点击查看代码
void solve() {
	int n;
	std::cin >> n;
    std::vector<int> p(n), q(n);
    for (int i = 0; i < n; ++ i) {
    	std::cin >> p[i];
    	-- p[i];
    }

    for (int i = 0; i < n; ++ i) {
    	std::cin >> q[i];
    	-- q[i];
    }

    std::vector<int> ans(n);
    for (int i = 0; i < n; ++ i) {
    	ans[q[i]] = q[p[i]];
    }

    for (int i = 0; i < n; ++ i) {
    	std::cout << ans[i] + 1 << " \n"[i == n - 1];
    }
}

D - Doubles

题意:有\(n\)个骰子,每个骰子有几率投出一些数,求两个骰子骰出相同数的最大概率。

\(n\)比较小,直接枚举两个骰子,然后计算它们可以骰出来的相同数的概率和。

点击查看代码
void solve() {
    int n;
    std::cin >> n;
    const int N = 1e5 + 5;
    std::vector<int> k(n);
    std::vector<std::map<int, int> > cnt(n);
    for (int i = 0; i < n; ++ i) {
    	std::cin >> k[i];
    	for (int j = 0; j < k[i]; ++ j) {
    		int x;
    		std::cin >> x;
    		++ cnt[i][x];
    	}
    }

    double ans = 0;

    for (int i = 0; i < n; ++ i) {
    	for (int j = i + 1; j < n; ++ j) {
    		double sum = 0;
    		for (auto & [x, y] : cnt[i]) {
    			if (cnt[j].count(x)) {
    				sum += 1.0 * y / k[i] * cnt[j][x] / k[j];
    			}
    		}
    		ans = std::max(ans, sum);
    	}
    }

    std::cout << std::fixed << std::setprecision(12);
    std::cout << ans << "\n";
}

E - Cables and Servers

题意:给你一个图,你可以改变某些边的连接端点使得整个图联通。

先求出每个联通块,然后每个联通块只需要\(size - 1\)条边,其中\(size\)是联通块的大小,那么其余的边是多余,可以用并查集求联通块和每个联通块多余的边。然后按顺序枚举所有联通块,记录前面联通块的可用边,和前面的联通块编号,每次先把前面联通块的边来连当前联通块,如果没有则用当前联通块的边尽可能连前面的联通块,模拟讨论一下就行,然后记得把没用过的边加进来,以及更新前面的联通块。

点击查看代码
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, m;
    std::cin >> n >> m;
    std::vector<std::pair<int, int> > edges(m);
    DSU d(n);
    std::vector<int> id;
    for (int i = 0; i < m; ++ i) {
    	int u, v;
    	std::cin >> u >> v;
    	-- u, -- v;
    	edges[i] = {u, v};
    	if (!d.merge(u, v)) {
    		id.push_back(i);
    	}
    }

    std::vector<std::vector<int>> g(n);
    for (auto & i : id) {
    	g[d.find(edges[i].first)].push_back(i);
    }

    std::vector<std::array<int, 3>> ans;
    std::vector<int> a, b;
    for (int i = 0; i < n; ++ i) {
    	if (d.find(i) == i) {
    		if (a.size()) {
    			int j = a.back();
    			a.pop_back();
    			ans.push_back({j, edges[j].first, i});
    			d.merge(i, d.find(edges[j].first));
    			for (auto & j : g[i]) {
    				a.push_back(j);
    			}
    		} else if (g[i].size()) {
    			for (auto & j : g[i]) {
    				while (b.size() && d.same(b.back(), i)) {
    					b.pop_back();
    				}

    				if (!b.size()) {
    					a.push_back(j);
    				} else {
    					ans.push_back({j, edges[j].first, b.back()});
    					d.merge(b.back(), i);
    					b.pop_back();
    				}
    			}

    			b.push_back(i);
    		} else {
    			b.push_back(i);
    		}
    	}
    }

    std::cout << ans.size() << "\n";
    for (auto & [x, y, z] : ans) {
    	std::cout << x + 1 << " " << y + 1 << " " << z + 1 << "\n";
    }
}

F - Insert

题意:\(n\)个数依次加入序列,第\(i\)个放到第\(p_i\)个,那么第\(p_i\)\(i-1\)位置上的数都要往后移。求最终序列。

考虑从后往前做,那么最后一个数的位置是可以确定的。假设当前以及放好了\(i + 1\)\(n\),现在在放\(i\),那么\(i\)要一直被小于等于\(p_i\)的往后退,这个过程中同时\(p_i\)也要变大。那么如何求\(p_i\)最后的位置?考虑二分,如果\(p_i + sum(1, mid - 1) >= mid\)那么\(p_i\)的位置一定大于等于\(mid\),这是具有单调性的,因为\(p_i\)实际上是被一步一步推着往后移的。

点击查看代码
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, {});
    }

    void add(int x, T v) {
        for (int i = x; i <= n; i += i & -i) {
            tr[i] += v;
        }
    }

    T query(int x) {
        T res = 0;
        for (int i = x; i; i -= i & -i) {
            res += tr[i];
        }
        return res;
    }

    T sum(int l, int r) {
        return query(r) - query(l - 1);
    }
};

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

    Fenwick<int> tr(n);
    std::vector<int> ans(n + 1);
    for (int i = n; i >= 1; -- i) {
    	int l = a[i], r = n;
    	while (l < r) {
    		int mid = l + r + 1 >> 1;
    		if (a[i] + tr.query(mid - 1) >= mid) {
    			l = mid;
    		} else {
    			r = mid - 1;
    		}
    	}

    	int x = a[i] + tr.query(l - 1);
    	tr.add(x, 1);
    	ans[x] = i;
    }

    for (int i = 1; i <= n; ++ i) {
    	std::cout << ans[i] << " \n"[i == n];
    }
}
posted @ 2025-02-08 22:13  maburb  阅读(102)  评论(1)    收藏  举报