日常训练2025-1-20

日常训练2025-1-20

C. Game of Mathletes

https://codeforces.com/contest/2060/problem/C

思路(小巧思)

不能说想像数之和,只能说太像两数之和了。博弈的题面就是唬人的。

代码

#include <bits/stdc++.h>

typedef std::pair<long long, long long> pll;
typedef std::pair<int, int> pii;
#define INF 0x3f3f3f3f
#define MOD 998244353
using i64 = long long;
const int N = 1e5+5;

void solve(){
	int n, k;
	std::cin >> n >> k;

	std::vector<int> a(n);
	for (int i = 0; i < n; i++) std::cin >> a[i];

	std::sort(a.begin(), a.end());
	std::map<int, int> mp;
	for (int i = 0; i < n; i++){
		mp[a[i]] += 1;
	}

	i64 ans = 0;
	for (auto [x, y] : mp){
		if (mp[k-x] != 0){
			ans += std::min(y, mp[k-x]);
		}
	}
	// for (auto e : a){
	// 	std::cout << e << ' ';
	// }	
	// std::cout << '\n';

	std::cout << ans / 2 << '\n';
}

signed main()
{
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);
	std::cout<<std::setiosflags(std::ios::fixed)<<std::setprecision(2);
	int t = 1, i;
	std::cin >> t;
	for (i = 0; i < t; i++){
		solve();
	}
	return 0;
}

D. Subtract Min Sort

https://codeforces.com/contest/2060/problem/D

思路(贪心)

一个地方操作后一定会产生一个零,所以就需要保证前缀也全是零才行。那么我能就能得到,不管前面的是否不递减,都应该让他们变成零,后面的才可以继续变零。

所以就是贪心的题,能操作就操作,最后判断一下是否有序即可。

代码

#include <bits/stdc++.h>

typedef std::pair<long long, long long> pll;
typedef std::pair<int, int> pii;
#define INF 0x3f3f3f3f
#define MOD 998244353
using i64 = long long;
const int N = 1e5+5;

void solve(){
	int n;
	std::cin >> n;

	std::vector<int> a(n);
	for (int i = 0; i < n; i++) std::cin >> a[i];

	for (int i = 0; i < n - 1; i++){
		
		int x = std::min(a[i], a[i+1]);
		a[i] -= x;
		a[i+1] -= x;
	}

	// for (auto e : a){
	// 	std::cout << e << ' ';
	// }
	// std::cout << '\n';

	for (int i = 0; i < n - 1; i++){
		if (a[i] > a[i+1]){
			std::cout << "No\n";
			return;
		}
	}

	std::cout << "Yes\n";
}

signed main()
{
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);
	std::cout<<std::setiosflags(std::ios::fixed)<<std::setprecision(2);
	int t = 1, i;
	std::cin >> t;
	for (i = 0; i < t; i++){
		solve();
	}
	return 0;
}

C小红走网格

https://ac.nowcoder.com/acm/contest/100253/C

思路(裴蜀定理)

题目又是经典的\(ax + by = d\)。所以终点的两个维度一定要是对应两个维度的步长的最大公约数的倍数,不然无解。

代码

#include <bits/stdc++.h>

typedef std::pair<long long, long long> pll;
typedef std::pair<int, int> pii;
#define INF 0x3f3f3f3f
#define MOD 998244353
using i64 = long long;
const int N = 1e5+5;

void solve(){
	int sx, sy;
	std::cin >> sx >> sy;

	std::array<int, 4> a;
	for (int i = 0; i < 4; i++){
		std::cin >> a[i];
	}

	int gg_x, gg_y;
	gg_x = std::gcd(a[2], a[3]);
	gg_y = std::gcd(a[0], a[1]);

	int ans = 0;
	if (sx % gg_x == 0){
		ans += 1;
	}
	if (sy % gg_y == 0){
		ans += 1;
	}

	if (ans == 2){
		std::cout << "YES\n";
	}else{
		std::cout << "NO\n";
	}
}

signed main()
{
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);
	std::cout<<std::setiosflags(std::ios::fixed)<<std::setprecision(2);
	int t = 1, i;
	std::cin >> t;
	for (i = 0; i < t; i++){
		solve();
	}
	return 0;
}

D隐匿社交网络

https://ac.nowcoder.com/acm/contest/100253/D

思路(并查集)

题目给你挑明了是并查集。还有一个难关是如何快速判断两个节点是否在一个集合,暴力枚举肯定不行。题目一般会隐含一些范围小的量,只需要往小的量上面优化就行。

这道题就是位数信息。一个数最多只有63个二进制位,一旦两个数在某一位上都为1,那么就满足题目中在一个集合里的条件。所以我们只需要把所有数根据第 i 为是否为1,装到63个盒子中,最终在一个盒子里的一定在一个集合里。

代码

#include <bits/stdc++.h>

typedef std::pair<long long, long long> pll;
typedef std::pair<int, int> pii;
#define INF 0x3f3f3f3f
#define MOD 998244353
using i64 = long long;
const int N = 1e5+5;

struct DSU {
    std::vector<int> f, siz;
    
    DSU() {}
    DSU(int n) {
        init(n);
    }
    
    void init(int n) {
        f.resize(n);
        std::iota(f.begin(), f.end(), 0);
        siz.assign(n, 1);
    }
    
    int find(int x) {
        while (x != f[x]) {
            x = f[x] = f[f[x]];
        }
        return x;
    }
    
    bool same(int x, int y) {
        return find(x) == find(y);
    }
    
    bool merge(int x, int y) {
        x = find(x);
        y = find(y);
        if (x == y) {
            return false;
        }
        siz[x] += siz[y];
        f[y] = x;
        return true;
    }
    
    int size(int x) {
        return siz[find(x)];
    }
};

void solve(){
	int n;
	std::cin >> n;

	DSU dd(n);
	std::vector<i64> a(n);
	for (int i = 0; i < n; i++){
		std::cin >> a[i];
	}

	std::vector<std::vector<pll>> g(64, std::vector<pll>());
	for (int i = 0; i < 64; i++){
		for (int j = 0; j < n; j++){
			if (a[j] >> i & 1){
				g[i].push_back({a[j], j});
			}
		}
	}

	for (int i = 0; i < 64; i++){
		if (g[i].size() == 0) continue;
		int x = g[i][0].second;
		int len = g[i].size();
		for (int j = 1; j < len; j++){
			dd.merge(x, g[i][j].second);
		}
	}

	int ans = 0;
	for (int i = 0; i < n; i++){
		ans = std::max(ans, dd.siz[i]);
	}

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

}

signed main()
{
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);
	std::cout<<std::setiosflags(std::ios::fixed)<<std::setprecision(2);
	int t = 1, i;
	std::cin >> t;
	for (i = 0; i < t; i++){
		solve();
	}
	return 0;
}

D - Squares in Circle

rating:棕色

https://atcoder.jp/contests/abc389/tasks/abc389_d

思路(枚举)

答案会从两个方向来,1.坐标轴上的点(不考虑原点),比较显然为\(4*(r - 1)\)个,2.原点的那个正方形为 1 个,3. 四个象限中的正方形,这个我们需要枚举一下。

由于具有对称性,所以我们只考虑第一象限的正方形。既然是在第一象限,所以我们只需要看\((i+0.5, j + 0.5)\)到原点的距离是否小于 r。可以得到方程

\((i+0.5)^2 + (j+0.5)^2 <= r^2\),由于其中有分数,不方便我们运算,我们左右两边同时乘以4,方程为\((2i + 1)^2 + (2j + 1)^2 <= 4*r^2\),然后用双指针枚举 i 求 j 的范围即可。随着 i 的增大, j 肯定是越来越小,而且不会回退的。

代码

#include <bits/stdc++.h>

typedef std::pair<long long, long long> pll;
typedef std::pair<int, int> pii;
#define INF 0x3f3f3f3f
#define MOD 998244353
using i64 = long long;
const int N = 1e5+5;

void solve(){
	i64 r;
	std::cin >> r;

	i64 ans = 0;
	ans += 4 * (r - 1) + 1;

	auto check = [&](i64 x, i64 y) ->bool {
		if (((2*x + 1) * (2 * x + 1) + (2*y + 1)*(2*y+1)) <= 4 * r * r) return true;
		else return false;
	};

	for (int i = 1, j = r; i <= r; i++){
		while (j > 0 && !check(i, j)) --j;
		ans += 4*j;
	}

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

signed main()
{
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);
	std::cout<<std::setiosflags(std::ios::fixed)<<std::setprecision(2);
	int t = 1, i;
	for (i = 0; i < t; i++){
		solve();
	}
	return 0;
}

E. Graph Composition

rating:1500

https://codeforces.com/contest/2060/problem/E

思路(图论+并查集)

本题我们用并查集维护图的连通性,如果图中两节点有路径可达,则在一个集合中。题中,G的联通性是确定的,而F还需要进行操作才能确定下来,所以我们先创建G的并查集。考虑F的每一条边,如果此边所连的两个节点在G是不联通的,则此边一定要删除,res += 1,如果此边所连的连个节点在G中是联通的,则此边可留,F的并查集要merge上这两条边。现在我们只考虑了G中不联通的F中也不能联通,还剩下G中联通的F中也要联通,这里我们可以根据两个图的联通分量计算,贡献即为 F的联通分量 - G的联通分量。

代码

#include <bits/stdc++.h>

typedef std::pair<long long, long long> pll;
typedef std::pair<int, int> pii;
#define INF 0x3f3f3f3f
#define MOD 998244353
using i64 = long long;
const int N = 1e5+5;

struct DSU {
    std::vector<int> f, siz;
    
    DSU() {}
    DSU(int n) {
        init(n);
    }
    
    void init(int n) {
        f.resize(n);
        std::iota(f.begin(), f.end(), 0);
        siz.assign(n, 1);
    }
    
    int find(int x) {
        while (x != f[x]) {
            x = f[x] = f[f[x]];
        }
        return x;
    }
    
    bool same(int x, int y) {
        return find(x) == find(y);
    }
    
    bool merge(int x, int y) {
        x = find(x);
        y = find(y);
        if (x == y) {
            return false;
        }
        siz[x] += siz[y];
        f[y] = x;
        return true;
    }
    
    int size(int x) {
        return siz[find(x)];
    }
};

void solve(){
	int n, m1, m2;
	std::cin >> n >> m1 >> m2;

	std::vector<std::array<int, 2>> ef(m1);
	for (int i = 0; i < m1; i++){
		int u, v;
		std::cin >> u >> v;
		u--, v--;
		ef[i] = {u, v};
	}

	int cg = n; // 维护G图的联通分量
	DSU dsg(n);
	for (int i = 0; i < m2; i++){
		int u, v;
		std::cin >> u >> v;
		u--, v--;
		cg -= dsg.merge(u, v);
	}

	i64 ans = 0;
	int cf = n; // 维护F图的联通分量
	DSU dsf(n);
	for (auto [u, v] : ef){
		if (dsg.same(u, v)){
			cf -= dsf.merge(u, v);
		}else{
			ans++;
		}
	}

	ans += cf - cg;
	std::cout << ans << '\n';
}

signed main()
{
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);
	std::cout<<std::setiosflags(std::ios::fixed)<<std::setprecision(2);
	int t = 1, i;
	std::cin >> t;
	for (i = 0; i < t; i++){
		solve();
	}
	return 0;
}
posted @ 2025-01-20 13:57  califeee  阅读(18)  评论(0)    收藏  举报