Loading

The 2024 ICPC Asia EC Regionals Online Contest (II)



A - Gambling on Choosing Regionals

题意

\(k\)场比赛,每场比赛每个大学至多\(c_i\)个队;总\(n\)个队伍,每队有分数与所属大学两个属性,每只队伍至多参加\(2\)场比赛。求各个队在最坏情况下的最优排名。

思路

最坏情况就是你打哪场,强队都去哪场,就选\(c_i\)小的场次,能让排名更靠前。对于每队能打两场,最坏情况,两场强队都跟着你,故考虑一场即可。

代码

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long

struct group
{
	int id, sc;
	string ut;
	bool operator < (const group a)
	{
		return sc > a.sc;
	}
};

void solve()
{
	int n, k, minn = LLONG_MAX;
	cin >> n >> k;
	for (int i = 0; i < k; i++)
	{
		int x;
		cin >> x;
		minn = min(minn, x); // 人最少的场次
	}
	vector<group> v(n);
	for (int i = 0; i < n; i++)
	{
		cin >> v[i].sc >> v[i].ut;
		v[i].id = i;
	}
	sort(v.begin(), v.end()); // 按分数降序排
	map<string, int> cnt; 
	vector<int> ans(n);
	int cur = 0;
	for (int i = 0; i < n; i++)
	{
		if (cnt[v[i].ut] < minn) // v[i]所属大学的名额还有
		{
			cur++;
			cnt[v[i].ut]++;
		}
		ans[v[i].id] = cur;
	}
	for (int i = 0; i < n; i++)
	{
		cout << ans[i] << endl;
	}
}


signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);

	int T = 1;
	//cin >> T;
	while (T--)
	{
		solve();
	}

	return 0;
}



F - Tourist

题意

初始值\(1500\),给\(n\)个数,相加后问能否\(\ge 4000\),能则输出在第几次恰好\(\ge 4000\),否则输出-\(1\)

思路

模拟。

代码

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long

void solve()
{
	int n, sum = 1500;
	cin >> n;
	for (int i = 0; i < n; i++)
	{
		int x;
		cin >> x;
		sum += x;
		if (sum >= 4000)
		{
			cout << i + 1 << endl;
			return;
		}
	}
	cout << -1 << endl;
}


signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);

	int T = 1;
	//cin >> T;
	while (T--)
	{
		solve();
	}

	return 0;
}


G - Game

题意

\(A\)\(B\)玩游戏,最初各有\(x\)\(y\)的筹码,各自赢的概率为\(p_0\)\(p_1\),平局概率为\(1-p_0-p_1\)。平局时立刻进行下一局;当赢家的筹码\(\ge\)输家的,游戏结束,此时的赢家获胜;每局的输家要失去赢家此时的筹码数。问\(A\)最后获胜的概率。

思路

其实只用考虑3种情况:\(x = y\)\(x < y\)\(x > y\),然后递归,加上辗转相除递归次数不会很多。除法用逆元处理。对于\(x>y\)的情况,\(B\)可以连赢\(cnt\)次(记这部分概率为\(P_B\)),然后接着递归;也可能在中途就输了,此时的概率就是\(1-P_B\),则最后的概率就是\(P_B*dfs(x-y*cnt,y)+(1-P_B) = (dfs(x-y*cnt,y)-1)*P_B+1\)

代码

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long

const int mod = 998244353;

int x, y, a, b, c, p0, p1, p2;

int qpow(int base, int x) // 快速幂
{
	int res = 1;
	while (x)
	{
		if (x & 1)
		{
			res *= base;
			res %= mod;
		}
		x >>= 1;
		base *= base;
		base %= mod;
	}
	return res;
}

int dfs(int x, int y)
{
	if (x == y) // 筹码一样输了就死
	{
		return p0;
	}
	if (x < y) // 筹码少,只能一直赢
	{
		int cnt = y / x;
		if (y % x == 0)
		{
			cnt--; // 留一次判最后的 x == y
		}
		return (dfs(x, y - x * cnt) * (qpow(p0, cnt) % mod)) % mod;
	}
	int cnt = x / y;
	if (x % y == 0)
	{
		cnt--; 
	}
	return ((dfs(x - y * cnt, y) - 1 + mod) % mod * qpow(p1, cnt) % mod + 1) % mod;

}

void solve()
{
	cin >> x >> y >> a >> b >> c; // 赢的概率是 a / c 和 b / c
	c = a + b; // 除去平局
	// 逆元
	p0 = a * qpow(c, mod - 2) % mod; // A赢
	p1 = b * qpow(c, mod - 2) % mod; // B赢

	cout << dfs(x, y) << endl;
}

signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);

	int T = 1;
	cin >> T;
	while (T--)
	{
		solve();
	}

	return 0;
}



I - Strange Binary

题意

给定非负整数\(n\),问是否能构造出序列\(A\)使得\(\sum_{i=0}^{31}2^i*a_i = n\),其中\(a_i\)满足:\(a_i = 1或0或 -1,a_i^2+a_{i+1}^2 \neq 0\)。有则输出任意满足要求的序列,否则输出\(NO\)

思路

对于\(a_i\),它代表的就是第\(i\)位上的\(1\)(\([0,31]\)位每一位上只有1个\(1\)),所以\(a_{31}\)必定是\(1\),否则结果就一定是负数。而显然\(0, 0, 0, 1, 1\)等价于\(1,-1,-1,-1,1\)所以每个\(01\)结构都能这样凑出来,即对于第\(i\)位的\(1\),可以转化成\(2^i = 2^{i+1} - 2^i\),此时\(a_{i+1}=1,a_i = -1\),这时又可以将第\(i+1\)位上的\(0\)抵消。构造过程中发现,\(4\)的倍数(末尾有两个以上连续的\(0\)),或者中间有两个以上连续的\(0\),不能构造。

代码

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long

void solve()
{
	int n;
	cin >> n;
	vector<int> a(32);
	int idx = 0;
	while (n)
	{
		if (n & 1)
		{
			a[idx++] = 1;
		}
		else
		{
			a[idx++] = 0;
		}
		n >>= 1;
	}
	for (int i = 0; i <= 30; i++)
	{
		if (a[i] == 1 && a[i + 1] == 0) // 0,1可以凑成1,-1
		{
			a[i + 1] = 1;
			a[i] = -1;
		}
	}
	for (int i = 0; i < 31; i++)
	{
		if (a[i] == 0 && a[i + 1] == 0) // 非法
		{
			cout << "NO" << endl;
			return;
		}
	}
	cout << "YES" << endl;
	for (int i = 0; i <= 31; i++)
	{
		cout << a[i] << ((i + 1) % 8 ? " " : "\n");
	}
}

signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);

	int T = 1;
	cin >> T;
	while (T--)
	{
		solve();
	}

	return 0;
}

J - Stacking of Goods

题意

\(n\)个物品,每个物品有重量\(w\)、体积\(v\)和可压缩度\(c\)三种属性。你要讲物品堆叠,一个物品在堆叠后的体积是\(v_i-c_i*W_i\),其中\(W_i\)是其上方所有物品的重量之和。求所有物品堆叠后总体积的最小值。

思路

对于一个物品,它的体积不会受其上方物品顺序的变化影响(\(W_i\)始终不变),用微扰法可证明只需按\(\frac c w\)升序排列即是最优:交换第\(i\)个和第\(i+1\)个物品,二者的体积分别变为\(v_i - c_i * (W_i+w_{i+1})\)\(v_{i+1} - c_{i+1} * (W_{i+1}-w_i)\),则\(△v = v_i - c_i * (W_i+w_{i+1})+v_{i+1} - c_{i+1} * (W_{i+1}-w_i)-(v_i - c_i * W_i-v_{i+1}) - (v_{i+1}-c_{i+1} * W_{i+1})\),得\(△v = -c_i * w_{i+1} + c_{i+1} * w_i\),当\(\frac {c_i} {w_i} < \frac {c_{i+1}} {w_{i+1}}\),即\(c_i * w_{i+1} < c_{i+1} * w_i\)时,\(△v > 0\),即一旦顺序改变,总体积增加,故按照\(\frac c w\)升序排列即是最优。

代码

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long

struct goods
{
	int w, v, c;
	bool operator < (const goods a)
	{
		return c * 1.0 / w < a.c * 1.0 / a.w; // 此处换为乘法最好
	}
};

void solve()
{
	int n, ans = 0, W = 0;
	cin >> n;
	vector<goods> v(n);
	for (int i = 0; i < n; i++)
	{
		cin >> v[i].w >> v[i].v >> v[i].c;
		ans += v[i].v;
	}
	sort(v.begin(), v.end());
	for (int i = 0; i < n; i++)
	{
		ans -= W * v[i].c;
		W += v[i].w;
	}
	cout << ans << endl;
}

signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);

	int T = 1;
	//cin >> T;
	while (T--)
	{
		solve();
	}

	return 0;
}



L - 502 Bad Gateway

题意

给定\(T\),有两种操作:\(-1\),或重置为\([1,T)\),求最优策略下,减少到\(0\)的期望步数。

思路

么最优的策略一定是选择一个阈值$ c ∈ [1, T]$,开始不停重置,一旦重置到一个小于 \(c\)
数就直接减到\(0\)。设重置到小于 c 的数的概率为 \(p = \frac c t\),根据期望线性性拆解到计算重置 \(k\) 次的概率之和,那么这个策略下的期望步数为:\(\frac{c-1}2+1+(1-p)+(1-p)^2+···=\frac{c-1}2+\frac t c\)
对勾函数形式,最小值在 \(c=\lceil\sqrt{2t}\rceil\)\(c=\lfloor\sqrt {2t} \rfloor\)处取得。

代码

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long

int gcd(int a, int b)
{
	return (b ? gcd(b, a % b) : a);
}

void solve()
{
	int t;
	cin >> t;
	int a = ceil(sqrt(2.0 * t));
	int b = floor(sqrt(2.0 * t));
	// 题目要求分式形式
	int na = 2 * t + a * (a - 1), da = 2 * a;
	int nb = 2 * t + b * (b - 1), db = 2 * b;
	if (na *db < nb * da)
	{
		int _gcd = gcd(na, da);
		cout << na / _gcd << ' ' << da / _gcd << endl;
	}
	else
	{
		int _gcd = gcd(nb, db);
		cout << nb / _gcd << ' ' << db / _gcd << endl;
	}
}

signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);

	int T = 1;
	cin >> T;
	while (T--)
	{
		solve();
	}

	return 0;
}


比赛链接 https://codeforces.com/gym/105358

posted @ 2024-09-24 15:05  _SeiI  阅读(384)  评论(0)    收藏  举报