Nyaan's Library

This documentation is automatically generated by online-judge-tools/verification-helper

View on GitHub

:heavy_check_mark: 彩色数
(graph/chromatic-number.hpp)

彩色数

頂点数$n$のグラフの彩色数を$\mathrm{O}(2^n n)$で求めるライブラリ。

概要

wata氏の指数時間アルゴリズムの資料noshi氏のライブラリの解説などに詳しい。

$\lbrace1,2,\ldots,m\rbrace$の部分集合からなる集合を$[m]$、実数の集合$R$を$[m]$に対して拡張したものを$R[U_m]$と置き、subset zeta convolutionを$R[U_m]$上の乗法とする。

この時、グラフ$G(V,E),|V|=n$が$k$彩色可能であることは、$d[s]=[s$が独立集合である$]$を満たす$d \in R[U_n]$について$d^k[\lbrace1,2,\ldots,n\rbrace] \neq 0$ であることと同値である。

よって、係数が非零になるまで$d^k$を昇順に計算していけば彩色数が求まる。これは愚直に計算すると$\mathrm{O}(2^n n^2)$だが、必要な係数は一つだけなのでゼータ/メビウス変換を陽に計算する必要はない事実を利用すると計算量を$\mathrm{O}(2^n n)$に改善できる。

Verified with

Code

#pragma once

#include <cstdint>
#include <utility>
#include <vector>
using namespace std;

namespace ChromaticNumberImpl {
using i64 = int64_t;
template <uint32_t mod>
int calc(int n, vector<pair<int, int>> hist) {
  for (int c = 1; c <= n; c++) {
    i64 sm = 0;
    for (auto& [i, x] : hist) sm += (x = i64(x) * i % mod);
    if (sm % mod != 0) return c;
  }
  return n;
}
}  // namespace ChromaticNumberImpl

template <typename G>
__attribute__((target("avx2"))) int ChromaticNumber(G& g) {
  int n = g.size();
  vector<int> adj(n), dp(1 << n);
  for (int i = 0; i < n; i++)
    for (auto& j : g[i]) adj[i] |= 1 << j, adj[j] |= 1 << i;
  dp[0] = 1;
  for (int i = 1; i < (1 << n); i++) {
    int j = __builtin_ctz(i);
    int k = i & (i - 1);
    dp[i] = dp[k] + dp[k & ~adj[j]];
  }
  vector<int> memo((1 << n) + 1);
  for (int i = 0; i < (1 << n); i++)
    memo[dp[i]] += __builtin_parity(i) ? -1 : 1;
  vector<pair<int, int>> hist;
  for (int i = 1; i <= (1 << n); i++)
    if (memo[i]) hist.emplace_back(i, memo[i]);
  return min(ChromaticNumberImpl::calc<1000000021>(n, hist),
             ChromaticNumberImpl::calc<1000000033>(n, hist));
}

/**
 * @brief 彩色数
 * @docs docs/graph/chromatic-number.md
 */
#line 2 "graph/chromatic-number.hpp"

#include <cstdint>
#include <utility>
#include <vector>
using namespace std;

namespace ChromaticNumberImpl {
using i64 = int64_t;
template <uint32_t mod>
int calc(int n, vector<pair<int, int>> hist) {
  for (int c = 1; c <= n; c++) {
    i64 sm = 0;
    for (auto& [i, x] : hist) sm += (x = i64(x) * i % mod);
    if (sm % mod != 0) return c;
  }
  return n;
}
}  // namespace ChromaticNumberImpl

template <typename G>
__attribute__((target("avx2"))) int ChromaticNumber(G& g) {
  int n = g.size();
  vector<int> adj(n), dp(1 << n);
  for (int i = 0; i < n; i++)
    for (auto& j : g[i]) adj[i] |= 1 << j, adj[j] |= 1 << i;
  dp[0] = 1;
  for (int i = 1; i < (1 << n); i++) {
    int j = __builtin_ctz(i);
    int k = i & (i - 1);
    dp[i] = dp[k] + dp[k & ~adj[j]];
  }
  vector<int> memo((1 << n) + 1);
  for (int i = 0; i < (1 << n); i++)
    memo[dp[i]] += __builtin_parity(i) ? -1 : 1;
  vector<pair<int, int>> hist;
  for (int i = 1; i <= (1 << n); i++)
    if (memo[i]) hist.emplace_back(i, memo[i]);
  return min(ChromaticNumberImpl::calc<1000000021>(n, hist),
             ChromaticNumberImpl::calc<1000000033>(n, hist));
}

/**
 * @brief 彩色数
 * @docs docs/graph/chromatic-number.md
 */
Back to top page