2024.3.9 训练记录(12)

文章介绍了四道竞赛编程题目,涉及模运算优化、区间动态规划以及质数判定在问题求解中的应用,展示了如何通过这些技术解决实际问题。
摘要由CSDN通过智能技术生成

昨天写课内作业鸽了一整天,该骂

CF 577B Modulo Sum

题目链接

好狡诈的题。。。本来想到dp但是一看数据范围不太能规划起来,看了题解原来需要先优化一下,如果n>m的话,前缀和取余的原理,一定会有两个前缀和取余的数相等,也就一定有答案,然后n<=m的情况再dp,dp[i][j]表示前 i 个数能不能组成 j 的倍数,转移时如果 dp[i - 1][j] 是true,那么选第 i 个数的话,就是 dp[i][(j + a[i]) % m] 为true,不选就是 dp[i][j] = true

#include <bits/stdc++.h>

using namespace std;

#define int long long
using i64 = long long;

typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef pair<PII, int> PIII;

const int N = (1 << 19);
const int mod = 998244353;
const int maxn = 1e6 + 10;
const int mod1 = 954169327;
const int mod2 = 906097321;
const int INF = 0x3f3f3f3f3f3f3f3f;

void solve()
{
	int n, m;
	cin >> n >> m;
	vector<int> a(n + 1);
	for (int i = 1; i <= n; i ++ )
	{
		cin >> a[i];
		a[i] = a[i] % m;
	}
	if (n > m)
	{
		cout << "YES\n";
		return;
	}
	vector<vector<bool>> dp(n + 1, vector<bool>(m + 1));
	for (int i = 1; i <= n; i ++ ) dp[i][a[i]] = true;
	for (int i = 1; i <= n; i ++ )
	{
		for (int j = 0; j <= m; j ++ )
		{
			if (dp[i - 1][j])
			{
				dp[i][(j + a[i]) % m] = true;
				dp[i][j] = dp[i - 1][j];
			}
		}
	}
	for (int i = 1; i <= n; i ++ )
	{
		if (dp[i][0])
		{
			cout << "YES\n";
			return;
		}
	}
	cout << "NO\n";
}

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

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

CF 607B Zuma

题目链接

一开始想跑n遍马拉车,但是突然发现局部最优并不是全局最优

然后这个数据范围也非常dp,于是想到区间dp

dp[i][j]表示[i, j]全消掉的最小时间,更新的时候取一个中间点 k ,dp[i][j] = dp[i][k] + dp[k + 1][j],同时,如果 a[i] 和 a[j] 相等,还有 dp[i][j] = dp[i + 1][j - 1]

#include <bits/stdc++.h>

using namespace std;

#define int long long
using i64 = long long;

typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef pair<PII, int> PIII;

const int N = (1 << 19);
const int mod = 998244353;
const int maxn = 1e6 + 10;
const int mod1 = 954169327;
const int mod2 = 906097321;
const int INF = 0x3f3f3f3f3f3f3f3f;

void solve()
{
	int n;
	cin >> n;
	vector<int> a(n + 1);
	for (int i = 1; i <= n; i ++ ) cin >> a[i];
	vector<vector<int>> dp(n + 1, vector<int>(n + 1, INF));
	for (int i = 1; i <= n; i ++ )
	{
		dp[i][i] = 1;
		if (i != n) dp[i + 1][i] = 1;
	}
	for (int len = 2; len <= n; len ++ )
	{
		for (int i = 1; i + len - 1 <= n; i ++ )
		{
			int j = i + len - 1;
			for (int k = i; k + 1 <= j; k ++ )
			{
				dp[i][j] = min(dp[i][k] + dp[k + 1][j], dp[i][j]);
			}
			if (a[i] == a[j]) dp[i][j] = min(dp[i][j], dp[i + 1][j - 1]);
		}
	}
	cout << dp[1][n] << '\n';
}

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

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

CF 1401D Maximum Distributed Tree

题目链接

这道题纯粹考每条边会经过多少次,这次遇到了就要记住了,size[v] * (n - size[v]),size用dfs预处理一下即可

次数多的赋值大,次数少的赋值小即可

#include <bits/stdc++.h>

using namespace std;

#define int long long
using i64 = long long;

typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef pair<PII, int> PIII;

const int N = (1 << 19);
const int mod = 1e9 + 7;
const int maxn = 1e6 + 10;
const int mod1 = 954169327;
const int mod2 = 906097321;
const int INF = 0x3f3f3f3f3f3f3f3f;

void solve()
{
	int n;
	cin >> n;
	vector<vector<int>> g(n + 1);
	vector<int> cnt;
	for (int i = 0; i < n - 1; i ++ )
	{
		int u, v;
		cin >> u >> v;
		g[u].push_back(v);
		g[v].push_back(u);
	}

	vector<int> size(n + 1, 1);
	function<void(int, int)> dfs = [&](int u, int fa)
	{
		for (int i = 0; i < g[u].size(); i ++ )
		{
			int j = g[u][i];
			if (j == fa) continue;
			dfs(j, u);
			size[u] += size[j];
			cnt.push_back(size[j] * (n - size[j]));
		}
	};
	
	dfs(1, -1);

	int m;
	cin >> m;
	vector<int> p(m);
	for (int i = 0; i < m; i ++ ) cin >> p[i];

	int ans = 0;
	if (m < n - 1)
	{
		sort(p.begin(), p.end(), greater<int>());
		sort(cnt.begin(), cnt.end(), greater<int>());

		for (int i = 0; i < m; i ++ ) ans = (ans + (p[i] * cnt[i]) % mod) % mod;
		for (int i = m; i < n - 1; i ++ ) ans = (ans + cnt[i]) % mod;
	}
	else
	{
		sort(p.begin(), p.end());
		sort(cnt.begin(), cnt.end());
		for (int i = n - 1; i < m; i ++ ) p[n - 2] = (p[n - 2] * p[i]) % mod;
		for (int i = 0; i < n - 1; i ++ ) ans = (ans + (p[i] * cnt[i]) % mod) % mod;
	}
	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 1329A Dreamoon Likes Coloring

题目链接

被dp伤透之后还是贪心大法好啊,,

首先发现如果所有 l 加在一起都不能到 n 的时候是一定不会实现的,然后尽可能让所有 p 小,也就是每次p只往右挪一个,如果有哪一次会超出 n 的话也是不能实现的

然后贪心去想,从后往前填数,每次都尽可能让p更小,直到剩下的p只能一个挨着一个为止(好像有点抽象,不如看代码好了)

#include <bits/stdc++.h>

using namespace std;

#define int long long
using i64 = long long;

typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef pair<PII, int> PIII;

const int N = (1 << 19);
const int mod = 1e9 + 7;
const int maxn = 1e6 + 10;
const int mod1 = 954169327;
const int mod2 = 906097321;
const int INF = 0x3f3f3f3f3f3f3f3f;

void solve()
{
	int n, m;
	cin >> n >> m;
	int sum = 0, pos = 1;
	vector<int> a(m);
	for (int i = 0; i < m; i ++ ) cin >> a[i], sum += a[i];
	if (sum < n)
	{
		cout << -1 << '\n';
		return;
	}
	for (int i = 0; i < m; i ++ )
	{
		if (pos + a[i] - 1 > n)
		{
			cout << "-1\n";
			return;
		}
		pos ++ ;
	}
	pos = n - a[m - 1] + 1;
	bool flag = false;
	vector<int> ans(m);
	ans[m - 1] = pos;
	for (int i = m - 2; i >= 0; i -- )
	{
		if (flag)
		{
			pos -- ;
			ans[i] = pos;
			if (pos + a[i] - 1 > n)
			{
				cout << -1 << '\n';
				return;
			}
			continue;
		}
		if ((pos - a[i] - 1) < i)
		{
			pos = i + 1;
			ans[i] = pos;
			flag = true;
		}
		else pos -= a[i], ans[i] = pos;
		if (pos + a[i] - 1 > n)
		{
			cout << -1 << '\n';
			return;
		}
	}
	for (auto t : ans) cout << t << ' ';
}

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

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

CF 584D Dima and Lisa

题目链接

看了下题解说是啥哥德巴赫猜想,头大了

总的来说就是,如果 n 是质数就直接输出一个 n ,如果不是就先把n减去3,变成合数,哥德巴赫猜想说>=4的合数可以拆成两个质数相加,枚举一下就可以

这一题的收获是:质数是密集的,每300个数就一定会出现质数

#include <bits/stdc++.h>

using namespace std;

#define int long long
using i64 = long long;

typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef pair<PII, int> PIII;

const int N = 1e8 + 3;
const int mod = 1e9 + 7;
const int maxn = 1e6 + 10;
const int mod1 = 954169327;
const int mod2 = 906097321;
const int INF = 0x3f3f3f3f3f3f3f3f;

bool check_prime(int x)
{
	for (int i = 2; i <= sqrt(x); i ++ )
	{
		if (x % i == 0) return false;
	}
	return true;
}

void solve()
{
	int n;
	cin >> n;
	if (check_prime(n)) cout << 1 << '\n' << n << '\n';
	else if (n == 4) cout << 2 << '\n' << 2 << ' ' << 2 << '\n';
	else if (n == 6) cout << 2 << '\n' << 3 << ' ' << 3 << '\n';
	else
	{
		cout << 3 << '\n' << 3 << ' ';
		n -= 3;
		for (int i = 2; i < n; i ++ )
		{
			if (check_prime(i) && check_prime(n - i))
			{
				cout << i << ' ' << n - i;
				return ;
			}
		}
	}
}

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

	int t = 1;
	// cin >> t;
	while (t -- )
	{
		solve();
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Texcavator

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

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

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

打赏作者

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

抵扣说明:

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

余额充值