10月20日补题记录

本文探讨了两个计算机科学竞赛中的算法问题。第一部分涉及图论,当给定偶数个点时,如何计算可以形成的树的数量。第二部分是动态规划的应用,寻找一串数字中相邻数字与的最大和。文章提供了详细的解题思路和代码实现。
摘要由CSDN通过智能技术生成

该篇文章主要记录打gym-102956的补题情况(后续还会有更新)

G - Biological Software Utilities

题目大意:给定N个点,两个点两个点组合,问能够组成多少棵树

解题思路:

对于N为奇数的时候,直接输出0即可(因为不满足题目给定的要求),接下来我们需要对N为偶数的时候进行考虑

因为N为偶数,同时每两个点需要合并成为一个点,那么实际使用的点只有N / 2个,根据定理那么这么多个组合起来的点总共能够构成(N / 2) ^ (N / 2 - 2)棵树,但是我们需要知道每个点是有序号的并且序号之间的组合也会对最后的结果产生一定的影响,那我们就进行一个推导

若只有两个点的时候,我们只能组成1 - 2这一种组合,若有四个点的时候我们可以组成 1 - 2,1 - 3,1 -4这三种组合,若有6个点的时候我们可以组成(1 - 2,3 - 4,5 - 6;1 - 2,3 - 5,4 - 6;1 - 2,3 - 6,4 -5;) * 5种组合,以此类推,对于任意c[n] = (n - 1) * c[n - 2]

接下来我们需要考虑点和点相连接的情况,若只有1 - 2,3 -4 时候,共有4种连发,若是6个点的时候,那么会有16种连发,那么连接的方法就有(4^ (n / 2) - 1)种

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 998244353;
ll n;
int main()
{
	cin >> n;
	if(n % 2 == 1)
	{
		printf("0\n");
	}
	else
	{
		ll fn = 1;
		ll hs = 1;
		ll mul = 1;
		ll ans; 
		for(ll i = 4;i <= n;i += 2)
		{
			fn = fn * (i - 1) % mod;
		}
		fn %= mod;
		for(int i = 1;i <= n / 2 - 2;++i)
		{
			hs *= (n / 2);
			hs %= mod;
		}
		hs %= mod;
		for(int i = 1;i <= n / 2 - 1;++i)
		{
			mul *= 4 % mod;
			mul %= mod;
		}
		ans = 1;
		ans = fn * hs % mod * mul % mod;
		cout << ans<<"\n";
	}
	return 0;
}

D - Bank Security Unification

题目大意:给定一串数字,在里面取一个子序列,使得相邻数字的与的和最大

解题思路:首先我们可以思考对于任意一个位置上面的f[n],我们都可以推出一个递推式子,假设不取这个位置,f[n] = f[n - 1];如果取这个位置的话,f[n] = max(f[n],f[k] + a[k] & a[i]);如果我们需要强行遍历一遍,那我们需要o(n ^ 2)的时间复杂度;但是我们可以思考一下,一个位置上的某一个二进制数位是1,他的最大值只可能与之前最近的哪个相同数位上是1的&。如果有三个数字分别是11111 10000 11111,如果我们只选取1 3最后结果是11111,但是我们选取1 2 3则是10000 + 10000就是100000,根据这个我们可以优化相应的时间

#include <bits/stdc++.h>
#define FAST std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
using namespace std;
const int maxn = 1e6 + 10;
typedef long long ll;
ll a[maxn];
ll f[maxn],pre[maxn];
int n;
int main()
{
	FAST
	cin >> n;
	for(int i = 1;i <= n;++i)
	{
		cin >> a[i];
	}
	for(int i = 1;i <= n;++i)
	{
		f[i] = f[i - 1];
		for(int j = 0;j <= 40;++j)
		{
			if(a[i] >> j & 1)
			{
				f[i] = max(f[i],f[pre[j]] + (a[i] & a[pre[j]]));
				pre[j] = i;
			}
		}
	}
	cout << f[n] <<"\n";
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值