2024.4.1-4.2 训练记录(24)

文章介绍了三道CF比赛题目,涉及三角形数量计算、区间处理和动态规划技巧。第一题是基础版本,第二题考虑奇数点带来的额外三角形,第三题是关于绘画问题的dp策略。
摘要由CSDN通过智能技术生成

复活了

CF 1942C1 Bessie’s Birthday Cake (Easy Version)

题目链接

在所有点之内的三角形数量是 x-2,之后判断每两个点之间间距,中间如果有一个点就加一个三角形

#include <bits/stdc++.h>

using namespace std;

#define int long long
using i64 = long long;

typedef pair<int, int> PII;
typedef pair<int, char> PIC;
typedef pair<double, double> PDD;
typedef pair<int, PII> PIII;
typedef pair<int, pair<int, bool>> PIIB;

const int N = 3e5 + 10;
const int mod = 1e9 + 7;
const int maxn = 1e6 + 10;
const int base = 131;
const int mod1 = 1e12 + 39;
const int mod2 = 1e9 + 9;
const int INF = 0x3f3f3f3f3f3f3f3f;

void solve()
{
	int n, x, y;
	cin >> n >> x >> y;
	vector<int> a(x);
	for (int i = 0; i < x; i ++ ) cin >> a[i];
	sort(a.begin(), a.end());
	int ans = x - 2;
	for (int i = 0; i < x; i ++ )
	{
		if (i == x - 1)
		{
			if (n - a[i] + a[0] - 1 == 1) ans ++ ;
		}
		else
		{
			if (a[i + 1] - a[i] == 2) ans ++ ;
		}
	}
	cout << ans << '\n';
}

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

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

CF 1942C2 Bessie’s Birthday Cake (Hard Version)

题目链接

之前的思路还是和c1一样,不同的是,如果两个点之间的点数是奇数,那我们可以获得 2n + 1 个新三角形(每加一个点获得两个三角形,最后获得三个三角形),偶数的话只能获得 2n(每加一个点获得两个三角形),所以优先选小的奇数

#include <bits/stdc++.h>

using namespace std;

#define int long long
using i64 = long long;

typedef pair<int, int> PII;
typedef pair<int, char> PIC;
typedef pair<double, double> PDD;
typedef pair<int, PII> PIII;
typedef pair<int, pair<int, bool>> PIIB;

const int N = 3e5 + 10;
const int mod = 1e9 + 7;
const int maxn = 1e6 + 10;
const int base = 131;
const int mod1 = 1e12 + 39;
const int mod2 = 1e9 + 9;
const int INF = 0x3f3f3f3f3f3f3f3f;

void solve()
{
	int n, x, y;
	cin >> n >> x >> y;
	vector<int> a(x);
	for (int i = 0; i < x; i ++ ) cin >> a[i];
	sort(a.begin(), a.end());
	int ans = x - 2;
	for (int i = 0; i < x; i ++ )
	{
		if (i == x - 1)
		{
			if (n - a[i] + a[0] - 1 == 1) ans ++ ;
		}
		else
		{
			if (a[i + 1] - a[i] == 2) ans ++ ;
		}
	}
	vector<int> cnt1, cnt0;
	for (int i = 0; i < x; i ++ )
	{
		if (i == x - 1)
		{
			if (n - a[i] + a[0] - 1 > 1)
			{
				if ((n - a[i] + a[0] - 1) % 2 == 0) cnt0.push_back((n - a[i] + a[0] - 1) / 2);
				else cnt1.push_back((n - a[i] + a[0] - 1) / 2);
			}
		}
		else
		{
			if (a[i + 1] - a[i] > 2)
			{
				if ((a[i + 1] - a[i] - 1) % 2 == 0) cnt0.push_back((a[i + 1] - a[i] - 1) / 2);
				else cnt1.push_back((a[i + 1] - a[i] - 1) / 2);
			}
		}
	}
	sort(cnt1.begin(), cnt1.end());
	for (auto t : cnt1)
	{
		if (y >= t)
		{
			ans += 2 * t + 1;
			y -= t;
		}
		else
		{
			ans += 2 * y;
			y = 0;
			break;
		}
	}
	for (auto t : cnt0)
	{
		if (y >= t)
		{
			ans += 2 * t;
			y -= t;
		}
		else
		{
			ans += 2 * y;
			y = 0;
			break;
		}
	}

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

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

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

CF 1942D Learning to Paint

题目链接

算是一个可以记录的trick

首先根据数据范围基本可以确定是一道dp

dp[i][j][0/1] 表示在前 i 个数中,第 j 大的情况是多少,最后一位 0/1 表示第 i 位是否是某一段的开头,如果取 0 的话,第 i 位没有要求,如果取 1 的话,第 i 位一定要是下一段的开头,也就是第 i - 1 位是不能选的

更新时,我们枚举最后一段的开头,然后把最大的情况存进优先队列 q.push({a[j][i] + dp[j - 1][1][0], j, 1});,之后依次取出队头来更新 dp[i][j][1],并把原先次大的情况存进去(这里还没有完全理解)q.push({a[l][i] + dp[l - 1][key + 1][0], l, key + 1});.,之后更新 dp[i][j][0],这个很容易理解看代码就行,最后输出也一样看代码就能理解

#include <bits/stdc++.h>

using namespace std;

#define int long long
using i64 = long long;

typedef pair<int, int> PII;
typedef pair<int, char> PIC;
typedef pair<double, double> PDD;
typedef pair<int, PII> PIII;
typedef pair<int, pair<int, bool>> PIIB;

const int N = 3e5 + 10;
const int mod = 1e9 + 7;
const int maxn = 1e6 + 10;
const int base = 131;
const int mod1 = 1e12 + 39;
const int mod2 = 1e9 + 9;
const int INF = 0x3f3f3f3f3f3f3f3f;

void solve()
{
	int n, k;
	cin >> n >> k;
	vector<vector<int>> a(n + 1, vector<int>(n + 1));
	for (int i = 1; i <= n; i ++ )
	{
		for (int j = i; j <= n; j ++ )
		{
			cin >> a[i][j];
		}
	}
	vector<vector<vector<int>>> dp(n + 1, vector<vector<int>>(k + 1, vector<int>(2)));
	for (int i = 0; i <= n; i ++ )
	{
		for (int j = 1; j <= k; j ++ )
		{
			dp[i][j][0] = dp[i][j][1] = -INF;
		}
	}
	dp[0][1][0] = 0;
	for (int i = 1; i <= n; i ++ )
	{
		priority_queue<array<int, 3>> q;
		for (int j = 1; j <= i; j ++ )
			q.push({a[j][i] + dp[j - 1][1][0], j, 1});
		for (int j = 1; j <= k; j ++ )
		{
			auto [v, l, key] = q.top();
			q.pop();
			dp[i][j][1] = v;
			if (key + 1 <= k)
			{
				q.push({a[l][i] + dp[l - 1][key + 1][0], l, key + 1});
			}
		}
		int p1 = 1, p2 = 1;
		for (int j = 1; j <= k; j ++ )
		{
			if (dp[i - 1][p1][0] > dp[i - 1][p2][1])
				dp[i][j][0] = dp[i - 1][p1 ++ ][0];
			else dp[i][j][0] = dp[i - 1][p2 ++ ][1];
		}
	}
	int p1 = 1, p2 = 1;
	for (int i = 0; i < k; i ++ )
	{
		if (dp[n][p1][0] > dp[n][p2][1]) cout << dp[n][p1 ++ ][0] << ' ';
		else cout << dp[n][p2 ++ ][1] << ' ';
	}
	cout << '\n';
}

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

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

CF

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Texcavator

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值