2022牛客多校第一场解题报告

这篇博客探讨了三个不同的编程问题,涉及区间覆盖、图形处理和最优化策略。首先,A题解释了如何通过计算区间距离之和解决村庄电话线问题。接着,B题提出了一个二维平面内的格子覆盖问题,通过求解上下边界交点找到最大覆盖区域。最后,D题利用勾股定理和三角函数解决了咖啡店与铁路炮之间的最佳位置问题。文章深入浅出地展示了如何运用数据结构和算法解决复杂问题。
摘要由CSDN通过智能技术生成

A.Villages: Landlines
 

思路:签到,直接划分区间,然后计算这些区间的距离之和即可

代码:

#include <iostream>
#include <algorithm>
#include <string>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <vector>
#include <queue>
#include <stack>
#include <set>
#include <bitset>
#include <map>
#include <unordered_map>
#include <unordered_set>
#include <time.h>
#include <assert.h>
#define IOS ios::sync_with_stdio(false), cin.tie(0)
#define int long long
#define ll long long
#define ull unsigned long long
#define PII pair<int, int>
#define PDI pair<double, int>
#define PDD pair<double, double>
#define debug(a) cout << #a << " = " << a << endl
#define all(x) (x).begin(), (x).end()
#define mem(x, y) memset((x), (y), sizeof(x))
#define lbt(x) (x & (-x))
#define SZ(x) ((x).size())
#define wtn(x) wt(x), printf("\n")
#define wtt(x) wt(x), printf(" ")
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#define MOD 998244353
#define eps 1e-8
int T = 1;
using namespace std;
inline int rd() {
    int x = 0, y = 1; char c = getchar();
    while (!isdigit(c)) { if (c == '-') y = -1; c = getchar(); }
    while (isdigit(c)) { x = (x << 3) + (x << 1) + (c ^ 48); c = getchar(); }
    return x *= y;
}
inline void wt(int x) {
    if (x < 0)x = -x, putchar('-'); ll sta[100], top = 0;
    do sta[top++] = x % 10, x /= 10; while (x);
    while (top) putchar(sta[--top] + '0');
}
const int N = 2e5 + 10;
int n;
struct rec {
	int l, r;
	bool operator< (const rec& i) {
		return l < i.l;
	}
} cor[N];
void solve() {
	cin >> n;
	for (int i = 1; i <= n; ++i) {
		int x, r;
		cin >> x >> r;
		cor[i].l = x - r, cor[i].r = x + r;
	}
	sort(cor + 1, cor + 1 + n);
	vector<PII> seg;
	int cur, l_;
	for (int i = 1, j = 0; i <= n; ++i) {
		l_ = cor[i].l;
		cur = cor[i].r;
		while (j + 1 <= n && cor[j + 1].l <= cur) {
			cur = max(cur, cor[j + 1].r);
			++j;
		}
		seg.emplace_back(l_, cur);
		i = j;
	}
	int ans = 0;
	for (int i = 1; i < SZ(seg); ++i) {
		ans += seg[i].first - seg[i - 1].second;
	}
	cout << ans << "\n";
}
signed main() {
	IOS;
	// cin >> T;
	while (T--) solve();
}

C-Grab the Seat!

 思路:

我们要求被留下的格子,可以对每一行单独求,注意到,每一行最多留下的格子取决于上边界延申的直线与这行的交点最左边与下边界延申的直线与这行的交点的最左边的靠左边的点决定(图中该行是蓝色部分).然后我们对每一行直线上下边界延申的直线求交点,然后取最左边的值,最后累加起来就是答案了.

技巧:分开枚举

代码:

#include <iostream>
#include <algorithm>
#include <string>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <vector>
#include <queue>
#include <stack>
#include <set>
#include <bitset>
#include <map>
#include <unordered_map>
#include <unordered_set>
#include <time.h>
#include <assert.h>
#define IOS ios::sync_with_stdio(false), cin.tie(0)
#define int long long
#define ll long long
#define ull unsigned long long
#define PII pair<int, int>
#define PDI pair<double, int>
#define PDD pair<double, double>
#define debug(a) cout << #a << " = " << a << endl
#define all(x) (x).begin(), (x).end()
#define mem(x, y) memset((x), (y), sizeof(x))
#define lbt(x) (x & (-x))
#define SZ(x) ((x).size())
#define wtn(x) wt(x), printf("\n")
#define wtt(x) wt(x), printf(" ")
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#define MOD 998244353
#define eps 1e-8
int T = 1;
using namespace std;
inline int rd() {
    int x = 0, y = 1; char c = getchar();
    while (!isdigit(c)) { if (c == '-') y = -1; c = getchar(); }
    while (isdigit(c)) { x = (x << 3) + (x << 1) + (c ^ 48); c = getchar(); }
    return x *= y;
}
inline void wt(int x) {
    if (x < 0)x = -x, putchar('-'); ll sta[100], top = 0;
    do sta[top++] = x % 10, x /= 10; while (x);
    while (top) putchar(sta[--top] + '0');
}
const int N = 2e5 + 10;
int n;
struct rec {
	int l, r;
	bool operator< (const rec& i) {
		return l < i.l;
	}
} cor[N];
void solve() {
	cin >> n;
	for (int i = 1; i <= n; ++i) {
		int x, r;
		cin >> x >> r;
		cor[i].l = x - r, cor[i].r = x + r;
	}
	sort(cor + 1, cor + 1 + n);
	vector<PII> seg;
	int cur, l_;
	for (int i = 1, j = 0; i <= n; ++i) {
		l_ = cor[i].l;
		cur = cor[i].r;
		while (j + 1 <= n && cor[j + 1].l <= cur) {
			cur = max(cur, cor[j + 1].r);
			++j;
		}
		seg.emplace_back(l_, cur);
		i = j;
	}
	int ans = 0;
	for (int i = 1; i < SZ(seg); ++i) {
		ans += seg[i].first - seg[i - 1].second;
	}
	cout << ans << "\n";
}
signed main() {
	IOS;
	// cin >> T;
	while (T--) solve();
}

D-Mocha and Railgun

思路:注意到,当AB方向平行与OQ方向时,答案最大.

然后用简单的勾股定理和三角函数知识可以解得ans

代码:

#include <iostream>
#include <algorithm>
#include <string>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <vector>
#include <queue>
#include <stack>
#include <set>
#include <bitset>
#include <map>
#include <unordered_map>
#include <unordered_set>
#include <time.h>
#include <assert.h>
#define IOS ios::sync_with_stdio(false), cin.tie(0)
#define int long long
#define ll long long
#define ull unsigned long long
#define PII pair<int, int>
#define PDI pair<double, int>
#define PDD pair<double, double>
#define debug(a) cout << #a << " = " << a << endl
#define all(x) (x).begin(), (x).end()
#define mem(x, y) memset((x), (y), sizeof(x))
#define lbt(x) (x & (-x))
#define SZ(x) ((x).size())
#define wtn(x) wt(x), printf("\n")
#define wtt(x) wt(x), printf(" ")
#define pi acos(-1)
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#define MOD 998244353
#define eps 1e-8
int T = 1;
using namespace std;
inline int rd() {
    int x = 0, y = 1; char c = getchar();
    while (!isdigit(c)) { if (c == '-') y = -1; c = getchar(); }
    while (isdigit(c)) { x = (x << 3) + (x << 1) + (c ^ 48); c = getchar(); }
    return x *= y;
}
inline void wt(int x) {
    if (x < 0)x = -x, putchar('-'); ll sta[100], top = 0;
    do sta[top++] = x % 10, x /= 10; while (x);
    while (top) putchar(sta[--top] + '0');
}
void solve() {
	double rc, x, y, d;
	scanf("%lf %lf %lf %lf", &rc, &x, &y, &d);
	double n = sqrt(x * x + y * y);
	double l = n - d, r = n + d;
	double d1 = sqrt(rc * rc - l * l), d2 = sqrt(rc * rc - r * r);
	double dd = abs(d1 - d2), len = sqrt(d * d * 4 + dd * dd);
	double cost = len / (rc * 2.0), theta = acos(cost);
	theta = pi - theta * 2.0;
	printf("%.6f\n", rc * theta);
}
signed main() {
	// IOS;
	// cin >> T;
	T = rd();
	while (T--) solve();
}

G.签到题

I.Chiitoitsu

思路:f(i,j)为还剩i张牌没拿,手中还剩j张单牌,然后有一个贪心策略就是,如果拿到跟手上配对以后就配对,没配对直接扔掉.有状态转移

f(i,j)=1+\left\{\begin{matrix} \frac{i-j*3}{i}*f(i-1,j) & \\ \frac{j*3}{i}*f(i-1,j-2)& \end{matrix}\right.

然后用记忆化搜索实现(期望dp倒着来)

代码:

#include <iostream>
#include <algorithm>
#include <string>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <vector>
#include <queue>
#include <stack>
#include <set>
#include <bitset>
#include <map>
#include <unordered_map>
#include <unordered_set>
#include <time.h>
#include <assert.h>
#define IOS ios::sync_with_stdio(false), cin.tie(0)
#define int long long
#define ll long long
#define ull unsigned long long
#define PII pair<int, int>
#define PDI pair<double, int>
#define PDD pair<double, double>
#define debug(a) cout << #a << " = " << a << endl
#define all(x) (x).begin(), (x).end()
#define mem(x, y) memset((x), (y), sizeof(x))
#define lbt(x) (x & (-x))
#define SZ(x) ((x).size())
#define wtn(x) wt(x), printf("\n")
#define wtt(x) wt(x), printf(" ")
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#define MOD 1000000007
#define eps 1e-8
int T = 1;
using namespace std;
inline int rd() {
    int x = 0, y = 1; char c = getchar();
    while (!isdigit(c)) { if (c == '-') y = -1; c = getchar(); }
    while (isdigit(c)) { x = (x << 3) + (x << 1) + (c ^ 48); c = getchar(); }
    return x *= y;
}
inline void wt(int x) {
    if (x < 0)x = -x, putchar('-'); ll sta[100], top = 0;
    do sta[top++] = x % 10, x /= 10; while (x);
    while (top) putchar(sta[--top] + '0');
}
int qmi(int a, int k, int p) {
	int res = 1;
	while (k) {
		if (k & 1) res = res * a % p;
		k >>= 1;
		a = a * a % p;
	}
	return res;
}
int inv(int x) {return qmi(x, MOD - 2, MOD);}
int f[150][30];
int dp(int i, int j) {
	if (j == -1) return 0;
	if (f[i][j] != -1) return f[i][j];
	int ans = 0;
	ans += (i - j * 3) * inv(i) % MOD * dp(i - 1, j) % MOD;
	ans += (j * 3) * inv(i) % MOD * dp(i - 1, j - 2) % MOD;
	return f[i][j] = (ans + 1) % MOD;
}
void solve() {
	string s;
	cin >> s;
	map<string, int> cnt;
	int cc = 0;
	for (int i = 0; i < SZ(s); i += 2) {
		string ts;
		ts += s[i]; ts += s[i + 1];
		if (cnt[ts]) ++cc;
		++cnt[ts];
	}
	cout << (dp(123, 13 - cc * 2) + MOD) % MOD << '\n';
}

signed main() {
	IOS;
	mem(f, -1);
	cin >> T;
	// T = rd();
	for (int i = 1; i <= T; ++i) {cout << "Case #" << i << ": "; solve();}
}

J.Serval and Essay

思路:如果发现一个集合只有两个元素,直接合并两个点能通向的点,然后修改那个点的from点,然后每次从小集合合并到大集合,也就是启发式合并,时间复杂度是O(nlogn)级别的.

代码:

#include <iostream>
#include <algorithm>
#include <string>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <vector>
#include <queue>
#include <stack>
#include <set>
#include <bitset>
#include <map>
#include <unordered_map>
#include <unordered_set>
#include <time.h>
#include <assert.h>
#define IOS ios::sync_with_stdio(false), cin.tie(0)
#define int long long
#define ll long long
#define ull unsigned long long
#define PII pair<int, int>
#define PDI pair<double, int>
#define PDD pair<double, double>
#define debug(a) cout << #a << " = " << a << endl
#define all(x) (x).begin(), (x).end()
#define mem(x, y) memset((x), (y), sizeof(x))
#define lbt(x) (x & (-x))
#define SZ(x) ((x).size())
#define wtn(x) wt(x), printf("\n")
#define wtt(x) wt(x), printf(" ")
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#define MOD 1000000007
#define eps 1e-8
int T = 1;
using namespace std;
inline int rd() {
    int x = 0, y = 1; char c = getchar();
    while (!isdigit(c)) { if (c == '-') y = -1; c = getchar(); }
    while (isdigit(c)) { x = (x << 3) + (x << 1) + (c ^ 48); c = getchar(); }
    return x *= y;
}
inline void wt(int x) {
    if (x < 0)x = -x, putchar('-'); ll sta[100], top = 0;
    do sta[top++] = x % 10, x /= 10; while (x);
    while (top) putchar(sta[--top] + '0');
}
//记录每个点的入点和出点
//当一个点的入点只有一个时,就合并两个出点的集合,最后最大的集合的大小就是能到达的点的最大数量
const int N = 2e5 + 10;
set<int> from[N], to[N];
int n, fa[N], sz[N];
int find(int x) {return x == fa[x] ? x : fa[x] = find(fa[x]);}
void merge(int x, int y) {
	x = find(x), y = find(y);
	if (x == y) return;
	if (SZ(to[x]) < SZ(to[y])) swap(x, y);
	fa[y] = x, sz[x] += sz[y];
	vector<PII> meg;
	for (auto t : to[y]) {
		to[x].insert(t);
		from[t].erase(y);
		from[t].insert(x);
		if (SZ(from[t]) == 1) meg.emplace_back(t, x);
	}
	for (auto [x, y] : meg) merge(x, y);
}
void solve() {
	n = rd();
	for (int i = 1; i <= n; ++i) {
		int k = rd();
		for (int j = 1, x; j <= k; ++j) {
			x = rd();
			to[x].insert(i);
			from[i].insert(x);
		}
	}
	for (int i = 1; i <= n; ++i) fa[i] = i, sz[i] = 1;
	for (int i = 1; i <= n; ++i) if (SZ(from[i]) == 1) merge(i, *from[i].begin());
	int ans = 0;
	for (int i = 1; i <= n; ++i) ans = max(ans, sz[i]);
	wtn(ans);
	for (int i = 1; i <= n; ++i) to[i].clear(), from[i].clear();
}
signed main() {
	// IOS;
	// cin >> T;
	T = rd();
	for (int i = 1; i <= T; ++i) { printf("Case #%d: ", i); solve();}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值