Loading

[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;
}
posted @ 2022-05-04 08:14  Aonynation  阅读(71)  评论(0编辑  收藏  举报