квадратные подмножества

Условие задачи

Условие

Дан массив длины . . Необходимо вычислить количество различных способов выбрать некоторое непустое подмножество его элементов так, чтобы их произведение было равно квадрату некоторого натурального числа.

Решение

Решение

В своё время мне понравилась эта задача.

Сделаем подсчёт каждого значения. (на это указывает ограничение в условие ).

Число является квадратом, если каждое встречается чётное количество раз в . ().

Количество простых чисел в диапазоне всего . Значит где-то тут будут маски!!

Пронумеруем простые числа в диапазоне числа

Напишем по маскам.

  1. Состояние — количество способов выбрать из массива значения и собрать . Где — если простое -ое простое число встречается нечётное количество раз в . если чётное, в противном случае.
  2. начальное значение.
  3. Переход :

-- количество способов выбрать нечётное количество элементов из

-- аналогично чётное.

Количество способов выбрать чётное количество элементов из множества , равняется , для нечётных тоже самое.

В переходе если мы берём любое чётное количество раз, мы не изменим .

В переходе логично изменим степени простых, которые пересекаются с маской от числа на противоположные. 4. Ответ — . ( так как мы пустое множество по условию не считаем, вроде?, мне уже лень думать)

int P[71];
int primeOrder[71];

int get_mask(int x) {
  if (x == P[x]) return (1 << primeOrder[x]);
  int mask = 0;
  while (x != 1) {
    int pr = P[x];
    int p = 0;
    while (x % pr == 0) {
      p++;
      x /= pr;
    }
    if (p % 2 == 1) {
      mask += (1 << primeOrder[pr]);
    }
  }
  return mask;
}

void solve() {
  int n;
  cin >> n;
  for (int i = 0; i < n; i++) {
    int x;
    cin >> x;
    cnt[x]++;
  }

  for (int i = 2; i <= 70; i++) {
    if (!P[i]) {
      for (int j = 2 * i; j <= 70; j += i) {
        P[j] = i;
      }
      P[i] = i;
    }
  }

  int cur = 0;
  for (int i = 2; i <= 70; i++) {
    if (P[i] == i) {
      primeOrder[i] = cur++;
    }
  }
  dp[0][0] = 1;
  for (int i = 0; i < 70; i++) {
    int CH = ch_comb(cnt[i + 1]);
    int NCH = nch_comb(cnt[i + 1]);
    int nxt_mask = get_mask(i + 1);
    for (int cur_mask = 0; cur_mask < (1 << cur); cur_mask++) {
      dp[i + 1][cur_mask ^ nxt_mask] =
          (dp[i + 1][cur_mask ^ nxt_mask] + 1LL * dp[i][cur_mask] * NCH) % mod;
      dp[i + 1][cur_mask] =
          (dp[i + 1][cur_mask] + 1LL * dp[i][cur_mask] * CH) % mod;
    }
  }
  cout << (dp[70][0] - 1 + mod) % mod;
}