zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    MVC filter
    常见的页面中两个div自适应等高CSS控制
    字符串排序 获取字符串中最长的单词
    js中“==”与“===”区别
    数组中字符串按照长度大小排序
    jquery中ajax使用
    关于ajax
    父级div宽度100%,子级一个div宽度固定,另一个宽度自适应
    window.onload与$(document).ready()区别
    盒模型
  • 原文地址:https://www.cnblogs.com/RiverHamster/p/sol-cf1566f.html
Copyright © 2011-2022 走看看