比赛链接:

https://ac.nowcoder.com/acm/contest/33186

A.Villages: Landlines

题意:

数轴上有 \(n\) 个点,每个点能覆盖左右长为 \(r\) 的地方,问将所有线段都连接还需多长的线段。

思路:

区间覆盖问题,将所有线段排序之后求值即可。

代码:

#include <bits/stdc++.h>
using namespace std;
#define LL long long
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	LL n;
	cin >> n;
	vector < pair<LL, LL> > range;
	for (int i = 0; i < n; i ++ ){
		LL x, r;
		cin >> x >> r;
		range.push_back({x - r, x + r});
	}
	sort(range.begin(), range.end());
	LL ans = 0, R = range[0].second;
	for (int i = 1; i < n; i ++ ){
		if (range[i].first > R){
			ans += range[i].first - R;
		}
		R = max(R, range[i].second);
	}
	cout << ans << "\n";
	return 0;
}

B.Spirit Circle Observation

SAM,fail 树,虚树

C.Grab the Seat!

模拟

D.Mocha and Railgun

题意:

给定一个圆和一个在圆内的点,从该点以任意角度向圆上发射一个长为 \(2d\) 的电磁炮,问最多能摧毁多长的圆。

思路:

由原点向电磁炮的边界做垂线。

要求的角就是绿色的两条线夹的角,所以先求出红色的角,再求蓝色的角。
设原点距离最近的一个边界的距离为 \(x\)
以弧度表示就为答案为 \(arccos(\frac{x}{r}) - arccos(\frac{r + 2d}{r})\)
对该函数求导,得到 \(- \frac{1}{r\sqrt{1 - \frac{x^2}{r^2}}} + \frac{1}{r\sqrt{1 - \frac{(x + 2d)^2}{r^2}}}\)
导数始终大于 0,所以随着 \(x\) 的增大,答案增大。\(x\) 最大的时候为原点和该点的连线。

代码:

#include <bits/stdc++.h>
using namespace std;
#define LL long long
void solve(){
	double r, x, y, d;
	cin >> r >> x >> y >> d;
	double dis = sqrt(x * x + y * y);
	double rad = acos((dis - d) / r) - acos((dis + d) / r);
	cout << rad * r << "\n";
}
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	cout << fixed << setprecision(10);
	LL T;
	cin >> T;
	while(T -- ){
		solve();
	}
	return 0;
}

G.Lexicographical Maximum

题意:

输出 1 到 \(n\) 中字典序最大的数。

思路:

如果 \(n\) 除了最后一位都是 9,那这个数的字典序最大。
否则,记 \(n\) 的长度为 \(s\)\(s - 1\) 个 9 的字典序最大。

代码:

#include <bits/stdc++.h>
using namespace std;
#define LL long long
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	string s;
	cin >> s;
	LL len = s.size();
	for (int i = 0; i < len - 1; i ++ ){
		if (s[i] != '9'){
			string t(len - 1, '9');
			cout << t << "\n";
			return 0;
		}
	}
	cout << s << "\n";
	return 0;
}

I.Chiitoitsu

题意:

打麻将,手中有十三张手牌,保证手中没有相同的三张手牌,每回合可以从牌堆中随机摸一张,然后判断手中有没有七个对子,接着打掉一张牌到弃牌堆(弃牌堆中的牌后续不会抽),每次都采用最优策略。每次询问告诉初始手牌,问抽到七小对的期望是多少。

思路:

概率 \(dp\)。设 \(dp_{i,j}\) 表示手中还有 \(i\) 张单牌,牌堆中还有 \(j\) 张牌,在最优决策下,凑出七小对的期望。
\(i\) 的取值只可能为 1, 3, 5, 7, 11, 13。
\(i\) == 1 时,抽到牌堆中任意对应的三张牌都可以完成,容易得到转移方程 \(dp_{i,j} = 1 + \frac{j - 3}{j}dp_{i,j - 1}\)
剩余的情况,无法一次完成,需要加上没抽到手牌中单牌对应的牌和抽到这两种情况。
得到转移方程 \(dp_{i,j} = 1 + \frac{j - 3i}{j}dp_{i, j - 1} + \frac{3i}{j}dp_{i - 2, j - 1}\)

代码:

#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int mod = 1e9 + 7;
LL dp[15][130];
LL qp(LL a, LL k, LL p){
	LL ans = 1;
	while (k){
		if (k & 1) ans = ans * a % p;
		k >>= 1;
		a = a * a % p;
	}
	return ans;
}
LL inv(LL x){
	return qp(x, mod - 2, mod);
}
void solve(){
	string s;
	cin >> s;
	LL n = s.size();
	map <string, LL> mp;
	for (int i = 0; i < n; i += 2){
		string t = "";
		t += s[i];
		t += s[i + 1];
		mp[t] ++ ;
	}
	LL cnt = 0;
	for (auto [x, y] : mp)
		cnt += (y == 1);
	cout << dp[cnt][123] << "\n";
}
void init(){
	for (int i = 1; i <= 13; i += 2 ){
		for (int j = 1; j <= 123; j ++ ){
			if (i == 1){
				dp[i][j] = (1 + (j - 3) * inv(j) % mod * dp[i][j - 1] % mod) % mod;
			}
			else{
				dp[i][j] = (1 + ((j - 3 * i) * inv(j) % mod * dp[i][j - 1] % mod + 3 * i * inv(j) % mod * dp[i - 2][j - 1] % mod) % mod) % mod;
			}
		}
	}
}
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	init();
	LL T;
	cin >> T;
	for (int i = 1; i <= T; i ++ ){
		cout << "Case #" << i << ": ";
		solve();
	}
	return 0;
}

J.Serval and Essay

题意:

\(n\) 个论点,当且仅当一个论点的前置论点都被证明的时候它才能被证明,现在已知各论点之间的 \(m\) 条推导关系,可以假设任意一个论点证明,问最多能推导多少个论点。

思路:

建立一个图,可知当一个点入度为 1 的时候,前面那个论点证明之后它就可以被证明,所以可以将这两个点合并。
这里可以用启发式合并的思想,将出度小的那个点合并到出度大的点。因为点的融合过程中,边可能会重复,所以可以用 \(set\) 去存边。

代码:

#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int N = 2e5 + 10;
set <LL> to[N], from[N];
LL n, p[N], sz[N];
LL get(LL x){
	return (x == p[x] ? x : (p[x] = get(p[x])) );
}
void unite(LL x, LL y){
	x = get(x);
	y = get(y);
	if (x == y) return;
	if (to[x].size() > to[y].size()){
		swap(x, y);
	}
	p[x] = y;
	sz[y] += sz[x];
	vector < pair<LL,LL> > v;
	for (auto t : to[x]){
		to[y].insert(t);
		from[t].insert(y);
		from[t].erase(x);
		if (from[t].size() == 1){
			v.push_back({y, t});
		}
	}
	for (auto [a, b] : v)
		unite(a, b);
}
void init(){
	for (int i = 1; i <= n; i ++ ){
		to[i].clear();
		from[i].clear();
		p[i] = i;
		sz[i] = 1;
	}
}
void solve(){
	cin >> n;
	init();
	for (int u = 1; u <= n; u ++ ){
		LL k;
		cin >> k;
		for (int i = 1; i <= k; i ++ ){
			LL v;
			cin >> v;
			to[v].insert(u);
			from[u].insert(v);
		}
	}
	for (int i = 1; i <= n; i ++ ){
		if (from[i].size() == 1){
			unite(i, *from[i].begin());
		}
	}
	LL ans = 0;
	for (int i = 1; i <= n; i ++ )
		ans = max(ans, sz[i]);
	cout << ans << "\n";
}
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	LL T = 1;
	cin >> T;
	for (int i = 1; i <= T; i ++ ){
		cout << "Case #" << i << ": ";
		solve();
	}
	return 0;
}
posted on 2022-07-19 20:23  Hamine  阅读(116)  评论(0编辑  收藏  举报