该篇文章主要记录打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;
}