CF1566F xor-quiz

(p(x))(x) 的所有素因子乘积。

询问 (x) 的结果等于询问 (p(x)) 的结果,因此只需要询问没有平方因子的数即可。这可以在给定的 (lceil 0.65n ceil) 次询问内完成。

考虑交互器怎么回答询问:

令(以下求和在 XOR 意义,即 (mod 2) 的向量下进行)

  • (g(x) = [x in S]x)
  • (h(x) = [p(x)=x]sum_{p(i)=x} g(x)) (即将贡献在 (p(i)) 处统计)

[egin{aligned} A(n) &= sum_{i=1}^c [gcd(i, n) = 1]h(i)\ &=sum_{d mid n} mu(d) sum_{d mid j, j le c} h(j) end{aligned} ]

因为求和是在 XOR 意义下进行的,且 (n) 无重因子((mu e 0)),所以 (mu(d) equiv 1),相当于先做(高维)后缀和再做(高维)前缀和。

考虑根据询问的结果,还原出 (h(x))。直接先做(高维)前缀差分再做(高维)后缀差分即可。


接着我们需要还原一组恰好 (c) 个元素的答案。

我们知道一组元素的 XOR 值,所以对每个值是否在集合中设变量,对 (log n) 位的值列方程(在 (mod 2) 意义下),用 Gauss 消元解出一组解。

一个 (h(x)) 等价类的大小最大可以达到 (260) 左右,解不一定是唯一的(当然这种情况很少出现),这时可以随机解出两组解,并使用压位背包找到可以符合个数限制的解。因为数据随机生成,类似于 (n = 0) 的极端情况是几乎不可能出现的,且解出的两组解一般差距不大,这样的随机方法在实践中可以很快找到一组答案。

#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#include <numeric>
#include <vector>
#include <cassert>
#include <bitset>
#include <random>
#include <chrono>
#include <unistd.h>
using namespace std;

#define LOG(f...) fprintf(stderr, f)
#define DBG(f...) printf("%3d: ", __LINE__), printf(f)
// #define DBG(f...) void()
#define all(cont) begin(cont), end(cont)
#ifdef __linux__
#define getchar getchar_unlocked
#define putchar putchar_unlocked
#endif

using ll = long long;

template <class T> void read(T &x) {
  char ch; x = 0;
  int f = 1;
  while (isspace(ch = getchar()));
  if (ch == '-') ch = getchar(), f = -1;
  do x = x * 10 + (ch - '0'); while(isdigit(ch = getchar()));
  x *= f;
}
template <class T, class ...A> void read(T &x, A&... args) { read(x); read(args...); }

const int N = 1000005;
const int SIZE = 270;
const int MAX_LEN = 40005;
using v2 = bitset<SIZE>;

mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());

bool np[1005];
int p[170], pc = 0;

void sieve(int n) {
  for (int i = 2; i <= n; ++i) {
    if (!np[i]) {
      p[pc++] = i;
      for (int j = i * 2; j <= n; j += i)
        np[j] = true;
    }
  }
}

vector<int> s[N];
int xs[N];
int len;
int n, c;
vector<int> P;

bitset<MAX_LEN> avail, aug;
int pre[MAX_LEN];
v2 e[20], _t[20];
vector<int> s1[MAX_LEN], s2[MAX_LEN];
bool type[MAX_LEN];

pair<v2, bool> eliminate(int n) {
  v2 sol;
  static int var[20];
  bool is_uniq = true;
  memset(var, -1, sizeof(var));
  for (int i = 0, j = 0; j < n && i < len; ++j) {
    int l = -1;
    for (int k = i; k < len; ++k)
      if (e[k].test(j)) { l = k; break; }
    if (l == -1) { is_uniq = false; sol.set(j, rng() & 1); continue; }
    if (l != i) swap(e[l], e[i]);
    var[i] = j;
    for (int k = i + 1; k < len; ++k)
      if (e[k].test(j)) e[k] ^= e[i];
    ++i;
  }
  for (int i = len - 1; i >= 0; --i)
    if (~var[i]) sol.set(var[i], ((sol & e[i]).count() & 1) ^ e[i].test(n));
  return {sol, is_uniq};
}

int cnt = 0;

bool solve() {
  int sum = 0;
  vector<int> uni_s;
  memset(type, 0, sizeof(type));
  avail.reset();
  avail.set(0);
  int cnt = 0;
  for (int p : P) {
    int n = s[p].size();
    if (n == 1) {
      if (xs[p]) {
        ++sum;
        uni_s.push_back(p);
      }
      continue;
    }
    for (int b = 0; b < len; ++b) {
      for (int i = 0; i < n; ++i)
        e[b].set(i, (s[p][i] >> b) & 1);
      e[b].set(n, (xs[p] >> b) & 1);
      _t[b] = e[b];
    }
    auto [sol, uniq] = eliminate(n);
    if (uniq) {
      for (int i = 0; i < n; ++i)
        if (sol.test(i))
          ++sum, uni_s.push_back(s[p][i]);
    }
    else {
      auto to_vec = [&](const v2 &v) -> vector<int> {
        vector<int> r;
        for (int i = 0; i < n; ++i)
          if (v.test(i)) r.push_back(s[p][i]);
        return r;
      };
      s1[cnt] = to_vec(sol);
      copy_n(_t, len, e);
      s2[cnt] = to_vec(eliminate(n).first);
      if (s1[cnt].size() > s2[cnt].size()) swap(s1[cnt], s2[cnt]);
      sum += s1[cnt].size();
      if (s1[cnt].size() == s2[cnt].size()) {
        uni_s.insert(uni_s.end(), all(s1[cnt]));
        continue;
      }
      aug = (avail << (s2[cnt].size() - s1[cnt].size())) & ~avail;
      for (int i = aug._Find_first(); i != (int)aug.size(); i = aug._Find_next(i))
        pre[i] = cnt;
      avail |= aug;
      ++cnt;
    }
  }
  if (c < sum || !avail.test(c - sum)) return false;
  vector<int> res = move(uni_s);
  for (int i = c - sum; i; i -= s2[pre[i]].size() - s1[pre[i]].size()) {
    type[pre[i]] = true;
    res.insert(res.end(), all(s2[pre[i]]));
  }
  for (int i = 0; i < cnt; ++i)
    if (!type[i])
      res.insert(res.end(), all(s1[i]));
  for (int x : res)
    printf("%d ", x);
  putchar('
');
  return true;
}

int main() {
#ifndef ONLINE_JUDGE
  freopen("input.txt", "r", stdin);
  freopen("output.txt", "w", stdout);
#endif
  sieve(1000);
  read(n, c);
  len = __lg(n) + 1;
  for (int i = 1; i <= n; ++i) {
    int r = 1;
    int num = i;
    for (int j = 0; j < pc; ++j) {
      if (num % p[j] == 0) {
        r *= p[j];
        do num /= p[j]; while (num % p[j] == 0);
      }
    }
    if (num > 1) r *= num;
    s[r].push_back(i);
  }

  for (int i = 1; i <= n; ++i)
    if (!s[i].empty()) P.push_back(i);
  printf("%d ", (int)P.size());
  for (int x : P)
    printf("%d ", x);
  putchar('
');
  fflush(stdout);
  for (int p : P)
    read(xs[p]);
  for (int i = n / 2; i >= 1; --i)
    if (!s[i].empty())
      for (int j = i * 2; j <= n; j += i)
        xs[j] ^= xs[i];
  for (int i = 1; i <= n; ++i)
    if (s[i].empty()) xs[i] = 0;
  for (int i = n / 2; i >= 1; --i)
    if (!s[i].empty())
      for (int j = i * 2; j <= n; j += i)
        xs[i] ^= xs[j];
  assert(xs[1] == 0 || xs[1] == 1);
  while (!solve());
  return 0;
}
原文地址:https://www.cnblogs.com/RiverHamster/p/sol-cf1566f.html