[JLOI2015]装备购买 题解 / 实数线性基学习笔记
看这道题之前,以为线性基只是支持异或的操作。。。
那么,我认为这道题体现出了线性基的本质:
就是说如何用最小的一个集合去表示所有出现的装备。
我们假设已经会使用线性基了,那么对于这道题该怎么办呢?
- 显然,根据贪心的思想,我们先把这些装备按照 \(cost\) 也就是花费从小向大排序。
- 我们从左往右 \(O(n)\) 扫一遍,如果可以插入线性基就插入然后加上答案的贡献。
- 如果不能插入,就一定不会造成贡献,这一点是很显然的。
所以,现在的关键问题是如何构建线性基。
其实我认为并没有那么困难。
因为我们已经知道异或线性基的写法,实数线性基直接套一下就好了。
这里先给出异或线性基的代码:
for (int i = 62; i >= 0; i--){
if (!(x >> (int)i)) continue;
if (!p[i]) {
p[i] = x;
break;
}
x ^= p[i];
}
观察一下可以发现,其实就是看当前位是有为一,再看线性基数组有没有被占用。
最后再把当前的 \(1\) 变成 \(0\) 的这一个简单的过程。
其实,实数的线性基也是一样的。
只是考虑如何把第 \(i\) 位消掉的问题,那直接高斯消元就可以了。
消完之后当前位上的就变成 \(0\) 了捏。
Code
#include <bits/stdc++.h>
#define file(a) freopen(a".out", "r", stdin), freopen(a".out", "w", stdout)
#define Enter putchar('\n')
#define quad putchar(' ')
const int N = 505;
const long double eps = 1e-6;
int n, m, p[N];
struct Node {
long double a[N];
int cost;
friend bool operator<(const Node &p, const Node &q) {
return p.cost < q.cost;
}
} node[N];
signed main(void) {
// file("1458");
std::cin >> n >> m;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
std::cin >> node[i].a[j];
for (int i = 1; i <= n; i++)
std::cin >> node[i].cost;
std::sort(node + 1, node + 1 + n);
int ans1 = 0, ans2 = 0;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (std::fabs(node[i].a[j]) < eps)
continue;
if (!p[j]) {
p[j] = i;
ans1 ++;
ans2 += node[i].cost;
break;
}
long double K = 1.0 * node[i].a[j] / node[p[j]].a[j];
for (int k = j; k <= m; k++)
node[i].a[k] -= K * node[p[j]].a[k];
}
}
std::cout << ans1 << " " << ans2 << std::endl;
}