比赛链接:

https://ac.nowcoder.com/acm/contest/24872

D.Strange_Fractions

题目大意:

给一个分数 \(\frac{p}{q}\),找出 a,b,使 \(\frac{p}{q} = \frac{a}{b} + \frac{b}{a}\),若没有,则输出 "0 0".

思路:

首先 \(\frac{p}{q}\) 这个分数需要先约分
然后,化简一下等式 \(\frac{p}{q} = \frac{a^2 + b^2}{a * b}\),可以得到第一个限制,\(p >= 2 * q\)
接着化简,容易得到 \(a = \frac{\sqrt[]{p + 2 * q} + \sqrt[]{p - 2 * q}}{2}\)\(b = \frac{\sqrt[]{p + 2 * q} - \sqrt[]{p - 2 * q}}{2}\),这里需要判断一下 \(p + 2 * q\)\(p - 2 * q\) 是不是完全平方数,因为答案要是有理的,这是第二个限制。

代码:

#include <bits/stdc++.h>
using namespace std;
#define IOS() ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int T, p, q;
void solve(){
	cin >> p >> q;
	int g = __gcd(p, q);
	p /= g;
	q /= g;
	if (p < 2 * q) cout << "0 0\n";
	else{
		int m = sqrt(p + 2 * q), n = sqrt(p - 2 * q);
		if (m * m != (p + 2 * q) || n * n != (p - 2 * q)) cout << "0 0\n";
		else cout << (m + n) / 2 << " " << (m - n) / 2 << "\n";
	}
}
int main(){
	IOS();
	cin >> T;
	while (T--)
		solve();
	return 0;
}

E.Strange_Integers

题目大意:

给了长为 \(n\) 的序列 \(a\) 和一个 \(k\),找出一个最长的序列,使 \(\lvert a_i - a_j \rvert >= k(1 <= i < j <= n)\)

思路:

将输入的序列排序,然后贪心找最长即可。

代码:

#include <bits/stdc++.h>
using namespace std;
#define IOS() ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n, k;
int main(){
	IOS();
	cin >> n >> k;
	vector <int> a(n);
	for (int i = 0; i < n; ++ i)
		cin >> a[i];
	sort(a.begin(), a.end());
	int p = 0, ans = 1;
	for (int i = 1; i < n; ++ i)
		if (a[i] - a[p] >= k){
			p = i;
			ans++;
		}
	cout << ans << "\n";
	return 0;
}

G.Edge Groups

题意:

给定一棵树,求出将整棵树拆成长度为 2 的路径的方案数。

思路:

树形 \(dp\),定义 \(f[i]\) 为以 \(i\) 为根的子树的划分方案数。
对于每一个节点 \(u\)
如果它的一个以根为 \(v\) 的子树的边的数量为奇数,即需要 \(u - v\) 这条边才能将整棵子树拆成长度为 2 的路径。
设子树的边加上 \(u - v\) 这条边,总共有 \(n\) 条边。
方案数为 \(\frac{C_n^2 * C_{n - 2}^2 * ... * C_2^2}{\frac{n}{2}!} = \frac{n!}{\frac{n}{2}! * 2^{\frac{n}{2}}} = 1 * 3 * ... * (n - 3) * (n - 1)\)(因为 \(n\) 是偶数)。
所以 \(f[u]\) = \(f[v]\) * 上述结果。
否则,不需要 \(u - v\) 就能将整棵子树拆分,所以 \(f[u]\) 直接乘上 \(f[v]\) 就行。

代码:

#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int N = 1e5 + 10, mod = 998244353;
LL n, f[N];
vector <LL> g[N];
bool dfs(LL u, LL fa){
    f[u] = 1;
    LL cnt = 0;
    for (auto v : g[u]){
        if (v == fa) continue;
        if ( !dfs(v, u) ) cnt ++ ;
        f[u] = f[u] * f[v] % mod;
    }
    for (int i = 1; i <= cnt; i += 2)
        f[u] = f[u] * i % mod;
    return cnt & 1;
}
int main(){
    ios::sync_with_stdio(false);cin.tie(0);
    cin >> n;
    for (int i = 1; i < n; i ++ ){
        LL u, v;
        cin >> u >> v;
        g[u].push_back(v);
        g[v].push_back(u);
    }
    dfs(1, 0);
    cout << f[1] << "\n";
    return 0;
}

I.Steadily Growing Steam

题目大意:

\(n\) 件物品,第 \(i\) 件物品的体积是 \(t_i\),价值是 \(v_i\),最多选出 \(k\) 件使它们的体积翻倍,然后选出若干件物品将其分为 体积和 相同的两堆,求选出物品的价值之和最大值

思路:

\(n\) 件物品中选出若干件物品,可以想到 背包
由于要分成两堆,我们可以假定,放入 第一堆 中是加入背包,放入 第二堆 中是拿出背包,也就是说放入第一堆中是 增加体积,放入第二堆中 减少体积,不论放入哪一堆,价值都是增加的。
dp[i][j][p]为 \(dp\) 方程,\(i\) 表示当前选择的是第几件物品, \(j\) 表示选中几件物品使其 体积翻倍\(p\)背包体积,就是一个简单背包啦。
可以发现,在求解的过程中,每一个物品只需要从上一个物品的状态转移过来,于是我们可以通过 滚动数组 优化掉一个维度。

代码:

#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int N = 105;
const int W = 1310;
LL n, k, w[N], v[N], f[N][W * 2], g[N][W * 2], ans = -(1LL << 60);
int main(){
	scanf("%lld %lld", &n, &k);
	for (int i = 1; i <= n; i++)
		scanf("%lld %lld", &v[i], &w[i]);
	memset(f, 233, sizeof(f));
	f[0][W] = 0;
	for (int i = 1; i <= n; i++){
		memcpy(g, f, sizeof(g));
		for (int j = 0; j < i && j <= k; j++)
			for (int p = 0; p < W * 2; p++){
				if (p > w[i]) g[j][p - w[i]] = max(g[j][p - w[i]], f[j][p] + v[i]);
				if (p > 2 * w[i]) g[j][p - 2 * w[i]] = max(g[j][p - 2 * w[i]], f[j][p] + v[i]);
				if (p + w[i] < W * 2) g[j][p + w[i]] = max(g[j][p + w[i]], f[j][p] + v[i]);
				if (p + 2 * w[i] < W * 2) g[j][p + 2 * w[i]] = max(g[j][p + 2 * w[i]], f[j][p] + v[i]);
			}
		memcpy(f, g, sizeof(f));
	}
	for (int i = 0; i <= k; i++)
		ans = max(ans, f[i][W]);
	cout << ans << "\n";
	return 0;
}
posted on 2021-12-04 21:33  Hamine  阅读(509)  评论(0编辑  收藏  举报