P r o b l e m \mathrm{Problem} Problem
给出 m m m 个不同的数 q i q_i qi,并且每个数都有个对应的费用 w i w_i wi。
现在要在 m m m 个数中选择一些数(每个数可使用任意多次,但是代价只会计算一次),用这些数组成一个长度为 n n n 的数列,并且满足任意两个不同种类的数,至少有一次相邻。
问最大费用。
S o l u t i o n \mathrm{Solution} Solution
为什么我有时候感觉CF的题目奇奇怪怪
显然这些数字具体是多少我们并不需要知道,我们只要关注权值即可。
如果我们选取 k k k 个数字,那么我们肯定求解权值前 k k k 大的。
我们现在需要求解这 k k k 个数字所能组成的长度最小的序列。
我们发现,我们假想中最理想的情况是:
- 序列中每新增一个数,就多能满足一对新的相邻情况。
那么我们可以转化为: k k k个点组成的完全图,是否存在欧拉路。
我们知道欧拉路存在的条件为:每一个节点的度数为偶数或存在两个点的度数为奇数。
因此,当 k k k 是奇数时,是一定存在欧拉路的,答案为 k ( k − 1 ) 2 + 1 \frac{k(k-1)}{2}+1 2k(k−1)+1
所以我们只需要考虑 k k k 是偶数的情况,其实就意味着序列中一定存在相邻的偶数,也就是说完全图中一定会有边走过两遍。如果我们让答案一定用欧拉路来表示的话(因为若图中存在欧拉路,就说明一定存在合法序列),我们可以让 k − 2 k-2 k−2个点两两连边,则答案为: k ( k − 1 ) 2 + k 2 \frac{k(k-1)}{2}+\frac{k}{2} 2k(k−1)+2k.
为什么答案要加上一呢?因为我们欧拉路求解的是边的数量,而序列的长度是点的数量。
C o d e \mathrm{Code} Code
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 3e6;
int n, m;
int a[N];
int read(void)
{
int s = 0, w = 0; char c = getchar();
while (c < '0' || c > '9') w |= c == '-', c = getchar();
while (c >= '0' && c <= '9') s = s*10+c-48, c = getchar();
return w ? -s : s;
}
int point(int x)
{
if (x % 2 == 1) return x * (x - 1) / 2 + 1;
if (x % 2 == 0) return x * (x - 1) / 2 + x / 2;
}
signed main(void)
{
n = read(), m = read();
for (int i=1;i<=m;++i) {
int q = read();
a[i] = read();
}
sort(a+1, a+m+1);
reverse(a+1, a+m+1);
int sum = 0;
for (int i=1;i<=m;++i)
{
if (point(i) > n) return cout << sum, 0;
sum += a[i];
}
cout << sum << endl;
return 0;
}