2024 ICPC湖北邀请赛暨湖北省赛 个人补题ABEFGHJL

Dashboard - The 2024 International Collegiate Programming Contest in Hubei Province, China - Codeforces

题目难度(过题):E A J B L H G F

榜单含打星队仅供参考

铜牌:4 321

银牌:5 419

金牌:9 1119

Problem - E - Codeforces

题意:加法。

// Code Start Here	
	int t;
	cin >> t;
	while(t--){
		int n , x , a , b;
		cin >> n >> x >> a >> b;
		cout << x * b + (n - x) * a <<endl;
	}

Problem - A - Codeforces

思路:我们考虑这样去最大化a , b:

对于a * \sqrt{b}的值是固定的,我们扩大b,减小a可以达到使a *b更大的效果。比如 4 * sqrt ( 1)和1 * sqrt(16) 都是4

所以我们直接让a = 1,b就是原式。

// Code Start Here	
	int t;
	cin >> t;
	while(t--){
		int x , y;
		cin >> x >> y;
		cout << 1 << " " << lcm(x , y) / gcd(x , y) << endl;
	}
	

Problem - J - Codeforces

思路:可以发现最后一定和平均数有关,考虑如何求分母,使用逆模运算即可

// Code Start Here	
	int n;
	cin >> n;
	int sum = 0;
	for(int i = 1;i<=n;i++){
		int x;
		cin >> x;
		sum += x;
		sum %= mod;
	}
	auto mod_pow = [&](int a ,int b)->int{
		int res = 1;
		a %= mod;
		while(b > 0){
			if(b & 1)res = res * a % mod;
			a = a * a % mod;
			b >>= 1;
		}
		return res;
	};
	int inv = mod_pow(n , mod - 2);
	int ans = (sum * inv) % mod;
	cout << ans << endl;
	

Problem - B - Codeforces

思路:思考什么情况下凸多边形面积最小,明显是三角形时面积最小,因此我们考虑如何找一个最小的三角形

观察到题目的三角形很少,而且比较密集,我们考虑可以枚举三角形的三个顶点A,B和C,找最小值,面积计算使用叉乘公式

constexpr double EPS = 1e-9;

template<class T>
struct Point {
	T x, y;	
	Point(T x_ = 0, T y_ = 0) : x(x_), y(y_) {}
	Point operator-(const Point& rhs) const {		return Point(x - rhs.x, y - rhs.y);}
	template<class U>	operator Point<U>() const {		return Point<U>(static_cast<U>(x), static_cast<U>(y));	}
	friend istream& operator>>(istream& is, Point& p) {	return is >> p.x >> p.y;}
	friend ostream& operator<<(ostream& os, const Point& p) {	return os << "(" << p.x << ", " << p.y << ")";	}
	static bool line(const Point& a, const Point& b, const Point& c) {	return cross(b - a, c - a) == 0;	}
	double area(const Point& b, const Point& c) const {	return fabs(cross(b - *this, c - *this)) / 2.0;}
	static long long cross(const Point& a, const Point& b) {	return (long long)a.x * b.y - (long long)a.y * b.x;}
};
int32_t main(){
	ios_base::sync_with_stdio(false);
	cin.tie(NULL);
	
	
// Code Start Here	
	int t;
	cin >> t;
	while(t--){
		int n;
		cin >> n;
		vector<Point<int>> pts(n);
		for (int i = 0; i < n; ++i)
			cin >> pts[i];
		
		double min_area = 1e18;
		bool found = false;
		
		for (int i = 0; i < n; ++i)
			for (int j = i + 1; j < n; ++j)
				for (int k = j + 1; k < n; ++k)
					if (!Point<int>::line(pts[i], pts[j], pts[k])) {
						double area = pts[i].area(pts[j], pts[k]);
						min_area = min(min_area, area);
						found = true;
					}
		
		if (!found) cout << -1 << endl;
		else cout << point(8) << min_area << endl;
	}
	
	return 0;
}

Problem - L - Codeforces

题意:

Walk Alone有一个只有正整数的数轴。从整数点a到整数点b的行走成本为lcm(a,b),其中lcm(a,b)表示整数a和b的最小公倍数。由于对整数1的憎恶,Walk Alone禁止任何人移动到小于或等于整数点1的位置。

给定两个整数abb,你需要计算从整数点a到b的最小成本。

思路:直接贪心或者dp没有思路,我们考虑建图处理。

于是题目可以转化为:在一个有n-1个点的带权完全无向图中,两个点之间的权值是lcm(x , y),求两个点之间的最短路。

考虑到数据范围1e7 ,所以Onlogn的堆优化版dijk也没办法跑,我们考虑有没有其他思路。

我们尝试分类讨论,假设a <= b

如果 a=b , cost = 0

如果 b mod a = 0 即b就是a的倍数,直接走到b,cost = b

如果gcd(a , b) > 1,有两种情况:

1. a -> b代价是lcm(a , b)

2. a -> gcd(a , b) -> b代价是a + b

如果gcd(a , b) = 1 , 即a 和 b 没有任何公共因数(即它们的质因子集合是完全不重叠的)

假设a = 15 = 3 *5 , b = 14 = 2 *7

则:gcd(15,14 ) =1

但如果中间走了z = 6。

lcm(15 , 6) = 30,lcm(6,14) = 42,cost = 72

反之,如果中间点是和a、b 都互质的质数(比如 2)2 与15无公共因子。2 与14也只有部分公共因子(但结构简单)

所以我们走中间点只选择小质数,如 2 是最好候选(越小越优)。或者选a 的最小质因数(记作 t1)也可以选b 的最小质因数(记作 t2)

即尝试以下路径:

a → t1 → b
a → t1 → t2 → b
a → t1 → 2 → t2 → b
a → 2 → b
a → t2 → b

// Code Start Here	
	int t;
	cin >> t;
	while(t--){
		auto check = [&](int x)->int{
			for (int i = 2; i * i <= x; ++i)
				if (x % i == 0) return i;
			return x;
		};
		int a , b;
		cin >> a >> b;
		if (a == b) cout << 0 << endl;
		else if (b % a == 0) cout << b <<endl;
		else if (__gcd(a, b) > 1) {
			cout << min((int)a + b, lcm((int)a, (int)b)) <<endl;
		}
		else{
			int t1 = check(a);
			int t2 = check(b);
			int ans = LLINF;
			ans = min(ans, lcm(a, t1) + lcm(t1, b));
			ans = min(ans, lcm(a, b));
			ans = min(ans, lcm(a, t1) + lcm(t1, t2) + lcm(t2, b));
			ans = min(ans, lcm(a, t1) + lcm(t1, 2) + lcm(2, t2) + lcm(t2, b));
			ans = min(ans, lcm(a, t1) + lcm(t1, 2) + lcm(2, b));
			ans = min(ans, lcm(a, 2) + lcm(2, t2) + lcm(t2, b));
			ans = min(ans, lcm(a, 2) + lcm(2, b));
			ans = min(ans, lcm(a, 2) + lcm(2, t1) + lcm(t1, t2) + lcm(t2, b));
			ans = min(ans, lcm(a, 2) + lcm(2, t1) + lcm(t1, b));
			ans = min(ans, lcm(a, t2) + lcm(t2, b));
			
			cout << ans <<endl;
		}
	}

Problem - H - Codeforces

题意:

有一个大小为 n x m 的网格。 有 k 个位置有鱼,每个位置 (xi, yi) 有 ai (≤ 3) 条鱼。 每次炸弹爆炸可以覆盖其上下左右共最多5个格子,每个格子只能捕一条鱼。 目标:用最少次数把所有鱼都炸干净。

思路:

炸弹每次只能抓一条鱼,而一个格子最多有 3 条鱼,意味着必须对单个格子的鱼数进行状态追踪。

思考状态建模思路:主要是如何高效的提取当前某个格子里的鱼的数量。我们可以用一个数组记录每个鱼格子里剩下的鱼数。例如,a = [2,1,0] 表示第 0 个鱼格子剩 2 条鱼,第 1 个剩 1 条,第 2 个已经没有了。但数组不能直接作为哈希或者数组下标使用,所以我们需要压缩这个数组为一个整数。由于每个格子的鱼数不超过 3,可以视为一个 4 进制数。

对于炸弹效果建模,任意一个炸弹位置 (x, y),它会影响到其上下左右和自身这最多 5 个格子。我们只关心它是否影响到当前的 k 个有鱼格子。对每一个有意义的投放位置,构造出一个向量,标记这颗炸弹能对每个鱼格子造成多大影响(最多 1 条鱼)

// Code Start Here	
	int n , m , k;
	cin >> n >> m >> k;
	vector<tuple<int,int,int>> fish(k);
	unordered_map<int,int> pos;
	vector<vector<int>> effects;
	auto encode = [&](vector<int> &a)->int{
		int res = 0;
		for(int i = k-1;i>=0;i--){
			res *= 4;
			res += a[i];
		}
		return res;
	};
	auto decode = [&](int res)->vector<int>{
		vector<int> a(k);
		for(int i = 0;i<k;i++){
			a[i] = res % 4;
			res /= 4;
		}
		return a;
	};
	for(int i = 0;i<k;i++){
		int x , y , z;
		cin >> x >> y >> z;
		fish[i] = {x , y , z};
		pos[x * 1000 + y] = i;
	}
	set<pair<int,int>> bomb;
	for(auto [x , y , z] : fish){
		bomb.insert({x , y});
		bomb.insert({x , y+1});
		bomb.insert({x , y-1});
		bomb.insert({x-1,y});
		bomb.insert({x+1,y});
	}
	for(auto [x , y ] : bomb){
		vector<int> effect(k , 0);
		for(int i = 0;i<9;i++){
			if(abs(dx8[i]) + abs(dy8[i]) <= 1){
				int nx = x + dx8[i];
				int ny = y + dy8[i];
				int key = nx *1000 + ny;
				if(pos.count(key)){
					effect[pos[key]] = 1;
				}
			}
		}
		effects.push_back(effect);
	}
	int max_val = 1;
	vector<int> init(k);
	for (int i = 0; i < k; ++i) {
		init[i] = get<2>(fish[i]);
		max_val *= 4;
	}
	vector<int> f(max_val, INF);
	int st = encode(init);
	f[st] = 0;
	for (int i = st; i >= 0; i--) {
		if (f[i] == INF) continue;
		auto cur = decode(i);
		for (auto eff : effects) {
			vector<int> nxt = cur;
			for (int j = 0; j < k; j++) {
				if (eff[j] && nxt[j] > 0) {
					nxt[j]--;
				}
			}
			int now = encode(nxt);
			f[now] = min(f[now], f[i] + 1);
		}
	}
	cout << f[0] << endl;

Problem - G - Codeforces

思路:模拟围棋吃子的过程。对于当前的棋子,找出当前这个棋子所属的连通块,判断这个连通块有没有“气”。如果没有气,把这个块上的所有棋子全部提掉,并统计这个块的大小(也就是吃掉了多少个棋子)。

// Code Start Here	
	
	constexpr int N = 20;
	int black = 0 , white = 0;
	vector<vector<int>> v(N,vector<int>(N,-1));
	int q;
	cin >> q;
	int total = 0;
	while(q--){
		int x_ , y_;
		cin >> x_ >> y_;
		int color = (total % 2) ? 0 : 1;
		auto bfs = [&](int x , int y ,int cor)->void{
			bool vis[N][N] = {0};
			vector<pair<int,int>> pos;
			queue<pair<int,int>> q;
			q.push({x , y});
			vis[x][y] = 1;
			bool f = false;
			while(!q.empty()){
				auto[xx , yy] = q.front();
				pos.push_back({xx , yy});
				q.pop();
				for(int i = 0;i<4;i++){
					int nx = xx + dx4[i];
					int ny = yy + dy4[i];
					if(nx >= 1 && ny >= 1 && nx <= 19 && ny <= 19){
						if(v[nx][ny] == -1){
							f = true;
							continue;
						}
						if(v[nx][ny] != cor)continue;
						if(vis[nx][ny])continue;
						vis[nx][ny] = true;
						q.push({nx , ny});
					}
				}
			}
			if(!f){
				if(cor)black += pos.size();
				else white += pos.size();
				for(auto [xx , yy] : pos)v[xx][yy] = -1;
			}
		};
		v[x_][y_] = color;
		black = white = 0;
		for(int i = 0;i<4;i++){
			int nx = x_ + dx4[i];
			int ny = y_ + dy4[i];
			if(nx >= 1 && ny >= 1 && nx <= 19 && ny <= 19 && v[nx][ny] == 1-color)bfs(nx , ny , 1-color);
		}
		bfs(x_,y_,color);
		cout << black << " " << white << endl;
		total++;
	}
	

Problem - F - Codeforces

题目描述较长,本质是一个可回溯可持久化线段树 + 合并模拟。

题意:有一个大小为 n 的数组 a[1..n],表示每本书的初始等级(1~q)。

你要支持以下操作:

1:查询最大等级:将区间 [l, r] 合并到不能再合并,输出最大等级。

2:加一本书 & 合并最大化合并次数:模拟加入一本新等级为 k 的书后,最多能合并多少次?以及代价(每次合并代价为 2^l+1)。

3:单点修改:将某本书改为等级 k。

4:回到历史版本:将当前序列恢复为第 t 次操作后的状态。

const int N = 1e6 + 5;
const int MOD = 1e9 + 7;

int n, m, A, p, q;
int a[N], rt[N];

inline int rnd() {
    return A = (7LL * A + 13LL) % 19260817;
}

struct PresidentTree {
    static constexpr int MAX_NODE = N * 50;

    struct node {
        int l = 0, r = 0;
        long long sum = 0;
    } tr[MAX_NODE];

    int cntNodes = 0;

    void modify(int prev, int &now, int pos, long long val, int l = 1, int r = n) {
        now = ++cntNodes;
        tr[now] = tr[prev];
        if (l == r) {
            tr[now].sum = val;
            return;
        }
        int mid = (l + r) >> 1;
        if (pos <= mid) modify(tr[prev].l, tr[now].l, pos, val, l, mid);
        else modify(tr[prev].r, tr[now].r, pos, val, mid + 1, r);
        tr[now].sum = tr[tr[now].l].sum + tr[tr[now].r].sum;
    }

    long long query(int now, int L, int R, int l = 1, int r = n) {
        if (!now || R < l || L > r) return 0;
        if (L <= l && r <= R) return tr[now].sum;
        int mid = (l + r) >> 1;
        return query(tr[now].l, L, R, l, mid) + query(tr[now].r, L, R, mid + 1, r);
    }
} T;

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

    cin >> n >> m >> A >> p >> q;

    for (int i = 1; i <= n; ++i) {
        a[i] = rnd() % q + 1;
        T.modify(rt[0], rt[0], i, 1LL << a[i]);
    }

    for (int i = 1; i <= m; ++i) {
        int opt = rnd() % p + 1;

        if (opt == 1) {
            rt[i] = rt[i - 1];
            int l = rnd() % n + 1, r = rnd() % n + 1;
            if (l > r) swap(l, r);
            long long sum = T.query(rt[i], l, r);
            int highest = 0;
            for (int j = 60; j >= 0; --j) {
                if ((sum >> j) & 1) {
                    highest = j;
                    break;
                }
            }
            cout << highest << '\n';
        } else if (opt == 2) {
            rt[i] = rt[i - 1];
            int l = rnd() % n + 1, r = rnd() % n + 1;
            if (l > r) swap(l, r);
            int k = rnd() % q + 1;
            long long sum = T.query(rt[i], l, r);
            long long cost = 0;
            for (int j = k; j <= 60; ++j) {
                if ((sum >> j) & 1) {
                    cost = (cost + (1LL << (j + 1))) % MOD;
                } else {
                    break;
                }
            }
            cout << cost << '\n';
        } else if (opt == 3) {
            int pos = rnd() % n + 1;
            int k = rnd() % q + 1;
            T.modify(rt[i - 1], rt[i], pos, 1LL << k);
        } else {
            int t = rnd() % i;
            rt[i] = rt[t];
        }
    }

    return 0;
}

<think>嗯,用户想要找2024ICPC昆明邀请赛的题目解析,但根据提供的引用内容,现有的资料只覆盖到2022年昆明的题目,比如引用[1]提到2020、2021年的ICPC和CCPC题目,以及引用[4]详细解析了2022昆明站的G题。不过用户询问的是2024年的,这可能还没有公开的解析,或者可能还没有举办。需要先确认是否存在2024年的比信息,或者是否用户可能记错了年份?比如,2024年的比可能尚未举行,或者相关信息还未被整理发布。 首先,检查提供的引用内容,发现最新的昆明站信息是2022年的,引用[4]讨论的是2022昆明G题。用户提到的2024年可能是个错误,或者他们希望提前获取信息。但通常比结束后才会有解析,所以需要告知用户目前没有相关资料,并建议他们关注官方渠道或后续的社区分享。同时,可以推荐现有的2022年昆明题解作为参考,帮助用户了解题型和考点,比如动态规划、期望计算等常见问题。 另外,用户可能对如何准备比感兴趣,可以建议他们分析历年题目类型,比如引用[1]提到的区域常考题型,或者引用[3]中提到的训练经验。需要确保回答准确,避免提供不存在的资源,同时提供有用的替代信息,帮助用户找到最近的可用资料。</think>关于2024ICPC昆明邀请赛题目解析,目前尚未有官方或主流平台发布完整的题目解析报告。根据历史信息推测,ICPC区域的题目解析通常会在后由参队伍、算法社区或教练团队逐步整理发布。以下是可参考的备建议和现有资源: --- ### 一、近期ICPC题型趋势参考 1. **动态规划与组合数学** 如2022年昆明站G题考察了**概率期望与递推关系**,需掌握马尔可夫链、状态转移方程的建立[^4] $$E = \sum_{i=1}^n \left( \frac{p_i}{1-p_i} \cdot \sum_{j \neq i} \frac{p_j}{1-p_i} \right)$$ 此类问题常需分析极限状态下的数学期望。 2. **数据结构优化** 近年区域常出现需要**线段树/树状数组维护区间性质**的题目,例如区间最值、历史版本查询等。 3. **图论与网络流** 包括最小割建模、分层图最短路等高级技巧,如2021年沈阳站曾出现网络流与二分答案结合的题目[^2]。 --- ### 二、获取解析的途径建议 1. **官方渠道** 关注ICPC官网及昆明站承办院校的事公告,解析可能通过**题解报告会**发布。 2. **算法社区** - **Codeforces**:搜索标签`[ICPC Kunming 2024]` - **知乎/掘金**:技术博主常撰写详细题解(例:2022年G题推导过程) 3. **训练平台** 尝试在**Codeforces Gym**或**牛客竞**题库中查找昆明站模拟题。 --- ### 三、历届昆明站真题参考 若需练习类似题型,可参考2022年昆明站题目: - **G题(豆子操作期望)**:结合概率论与递推,需推导稳定状态下的位置关系 - **B题(几何构造)**:通过坐标系变换简化多边形切割问题 ```python # 示例:概率期望计算的代码框架 def calculate_expected_value(probabilities): n = len(probabilities) expected = 0.0 for i in range(n): pi = probabilities[i] term = pi / (1 - pi) if pi != 1 else 0 sum_other = sum(pj / (1 - pi) for j, pj in enumerate(probabilities) if j != i) expected += term * sum_other return expected ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值