[状压DP][NTT] 2016 TCO Algorithm Algo Final - Division I, Level Three HamiltonianPaths

Description

给定一个 K 个点M条边的图,将这个图复制成 N 份,任意两份之间没有连边,求复制
得到的图的补图的哈密顿路径条数。
请输出答案对998244353取模的结果。

Solution

好神的题~
转化成某些边不能走。
考虑容斥,把题目转化为模板图中某些边必须要走。
fe 表示走了 e 条不合法的路径的方案数。
那么可以得到模板图缩了这些路径以后剩下x个点的方案数 gx
n 个图放在一起就是卷积的形式。
FFT快速幂可以得到所有点放在一起剩 x 个的点的方案数hx
然后容斥就可以通过 hx 得到 0 <script type="math/tex" id="MathJax-Element-16">0</script>对同类点在一起的方案数。

// BEGIN CUT HERE

// END CUT HERE
#line 5 "HamiltonianPaths.cpp"
#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int GR = 3;
const int K = 15;
const int N = 50505;
const int M = N * K << 2;
const int MOD = 998244353;

int f[1 << K][K], g[1 << K][K * K];
struct edge {
  int to, next;
  edge(int t = 0, int n = 0):to(t), next(n) {}
};
edge G[K * K * 2];
int head[K];
int inv[M], fac[M];
int n, m, k, Gcnt, x, y, U;
int w[2][M];
int R[M], ans[M];
int num, L, l, Ans;

class HamiltonianPaths {

public:

  inline void AddEdge(int from, int to) {
    G[++Gcnt] = edge(to, head[from]); head[from] = Gcnt;
    G[++Gcnt] = edge(from, head[to]); head[to]= Gcnt;
  }
  inline void Add(int &x, int a) {
    x = (x + a) % MOD;
  }
  inline int Pow(int a, int b) {
    int c = 1;
    while (b) {
      if (b & 1) c = (ll)c * a % MOD;
      b >>= 1; a = (ll)a * a % MOD;
    }
    return c;
  }
  inline int Inv(int x) {
    return Pow(x, MOD - 2);
  }
  void Pre(int n) {
    inv[1] = 1;
    for (int i = 2; i <= n; i++)
      inv[i] = (ll)(MOD - MOD / i) * inv[MOD % i] % MOD;
    fac[0] = inv[0] = 1;
    for (int i = 1; i <= n; i++) {
      fac[i] = (ll)fac[i - 1] * i % MOD;
      inv[i] = (ll)inv[i - 1] * inv[i] % MOD;
    }
    int g = Pow(GR, (MOD - 1) / n);
    int ig = Inv(g); num = n;
    w[0][0] = w[1][0] = 1;
    for (int i = 1; i <= n; i++) {
      w[0][i] = (ll)w[0][i - 1] * ig % MOD;
      w[1][i] = (ll)w[1][i - 1] * g % MOD;
    }
    for (int i = 1; i < n; i++)
      R[i] = (R[i >> 1] >> 1) | ((i & 1) << L);
  }
  inline void FFT(int *a, int n, int r) {
    static int x, y, INV;
    for (int i = 0; i < n; i++)
      if (R[i] > i) swap(a[i], a[R[i]]);
    for (int i = 1; i < n; i <<= 1)
      for (int j = 0; j < n; j += (i << 1))
    for (int k = 0; k < i; k++) {
      x = a[j + k];
      y = (ll)a[j + k + i] * w[r][num / (i << 1) * k] % MOD;
      a[j + k] = (x + y) % MOD;
      a[j + k + i] = (x - y + MOD) % MOD;
    }
    if (!r) {
      INV = Inv(n);
      for (int i = 0; i < n; i++)
    a[i] = (ll)a[i] * INV % MOD;
    }
  }

  int main(void) {
    cout << Ans << endl;
    return 0;
  }
  int countPaths(int k, vector <int> a, vector <int> b, int n) {  
    U = 1 << k; Pre(n * k); m = a.size();
    Gcnt = 0; memset(head, 0, sizeof head);
    Ans = 0;
    memset(f, 0, sizeof f);
    memset(g, 0, sizeof g);
    memset(ans, 0, sizeof ans);
    for (int i = 1; i <= m; i++)
      AddEdge(a[i - 1] + 1, b[i - 1] + 1);
    for (int i = 1; i <= k; i++) f[1 << i - 1][i] = 1;
    for (int S = 1; S < U; S++)
      for (int i = 1; i <= k; i++)
    if (((S >> i - 1) & 1) && f[S][i])
      for (int j = head[i]; j; j = G[j].next)
        if (!((S >> G[j].to - 1) & 1))
          Add(f[S | (1 << G[j].to - 1)][G[j].to], f[S][i]);
    for (int S = 1; S < U; S++)
      for (int i = 1; i <= k; i++)
    Add(*f[S], f[S][i]);
    g[0][0] = 1;
    for (int S = 0; S < U; S++)
      for (int e = 1; e <= k; e++)
    for (int T = S; T; T = (T - 1) & S)
      Add(g[S][e], (ll)g[S ^ T][e - 1] * *f[T] % MOD);
    for (l = 1; l <= n * k; l <<= 1) L++; l <<= 1;
    Pre(l);
    for (int i = 1; i <= k; i++) {
      Add(ans[i], (ll)g[U - 1][i] * inv[i] % MOD);
    }
    FFT(ans, l, 1);
    for (int i = 0; i < l; i++) ans[i] = Pow(ans[i], n);
    FFT(ans, l, 0);
    for (int i = 1; i <= n * k; i++) {
      if ((n * k - i) & 1) Add(Ans, MOD - (ll)fac[i] * ans[i] % MOD);
      else Add(Ans, (ll)fac[i] * ans[i] % MOD);
    }
    return Ans;
  }

  // BEGIN CUT HERE
public:
  void run_test(int Case) { if ((Case == -1) || (Case == 0)) test_case_0(); if ((Case == -1) || (Case == 1)) test_case_1(); if ((Case == -1) || (Case == 2)) test_case_2(); if ((Case == -1) || (Case == 3)) test_case_3(); if ((Case == -1) || (Case == 4)) test_case_4(); if ((Case == -1) || (Case == 5)) test_case_5(); if ((Case == -1) || (Case == 6)) test_case_6(); }
private:
  template <typename T> string print_array(const vector<T> &V) { ostringstream os; os << "{ "; for (typename vector<T>::const_iterator iter = V.begin(); iter != V.end(); ++iter) os << '\"' << *iter << "\","; os << " }"; return os.str(); }
  void verify_case(int Case, const int &Expected, const int &Received) { cerr << "Test Case #" << Case << "..."; if (Expected == Received) cerr << "PASSED" << endl; else { cerr << "FAILED" << endl; cerr << "\tExpected: \"" << Expected << '\"' << endl; cerr << "\tReceived: \"" << Received << '\"' << endl; } }
  void test_case_0() { int Arg0 = 3; int Arr1[] = {0,1}; vector <int> Arg1(Arr1, Arr1 + (sizeof(Arr1) / sizeof(Arr1[0]))); int Arr2[] = {1,2}; vector <int> Arg2(Arr2, Arr2 + (sizeof(Arr2) / sizeof(Arr2[0]))); int Arg3 = 2; int Arg4 = 152; verify_case(0, Arg4, countPaths(Arg0, Arg1, Arg2, Arg3)); }
  void test_case_1() { int Arg0 = 12; int Arr1[] = {}; vector <int> Arg1(Arr1, Arr1 + (sizeof(Arr1) / sizeof(Arr1[0]))); int Arr2[] = {}; vector <int> Arg2(Arr2, Arr2 + (sizeof(Arr2) / sizeof(Arr2[0]))); int Arg3 = 10000; int Arg4 = 129246395; verify_case(1, Arg4, countPaths(Arg0, Arg1, Arg2, Arg3)); }
  void test_case_2() { int Arg0 = 5; int Arr1[] = {0,1,2,3,4}; vector <int> Arg1(Arr1, Arr1 + (sizeof(Arr1) / sizeof(Arr1[0]))); int Arr2[] = {1,2,3,4,0}; vector <int> Arg2(Arr2, Arr2 + (sizeof(Arr2) / sizeof(Arr2[0]))); int Arg3 = 1; int Arg4 = 10; verify_case(2, Arg4, countPaths(Arg0, Arg1, Arg2, Arg3)); }
  void test_case_3() { int Arg0 = 1; int Arr1[] = {}; vector <int> Arg1(Arr1, Arr1 + (sizeof(Arr1) / sizeof(Arr1[0]))); int Arr2[] = {}; vector <int> Arg2(Arr2, Arr2 + (sizeof(Arr2) / sizeof(Arr2[0]))); int Arg3 = 1; int Arg4 = 1; verify_case(3, Arg4, countPaths(Arg0, Arg1, Arg2, Arg3)); }
  void test_case_4() { int Arg0 = 4; int Arr1[] = {1,2,3,2,3,3}; vector <int> Arg1(Arr1, Arr1 + (sizeof(Arr1) / sizeof(Arr1[0]))); int Arr2[] = {0,0,0,1,1,2}; vector <int> Arg2(Arr2, Arr2 + (sizeof(Arr2) / sizeof(Arr2[0]))); int Arg3 = 1; int Arg4 = 0; verify_case(4, Arg4, countPaths(Arg0, Arg1, Arg2, Arg3)); }
  void test_case_5() { int Arg0 = 4; int Arr1[] = {1,2,3,2,3,3}; vector <int> Arg1(Arr1, Arr1 + (sizeof(Arr1) / sizeof(Arr1[0]))); int Arr2[] = {0,0,0,1,1,2}; vector <int> Arg2(Arr2, Arr2 + (sizeof(Arr2) / sizeof(Arr2[0]))); int Arg3 = 10006; int Arg4 = 33330626; verify_case(5, Arg4, countPaths(Arg0, Arg1, Arg2, Arg3)); }
  void test_case_6() { int Arg0 = 14; int Arr1[] = {0,4,0,0,0,12,2,2,9,2,2,3,3,3,3,4,8,4,5,5,10,11,6,12,10,13,10,13,12,13,11}; vector <int> Arg1(Arr1, Arr1 + (sizeof(Arr1) / sizeof(Arr1[0]))); int Arr2[] = {2,0,5,8,11,1,5,6,2,10,12,4,5,9,13,7,4,13,6,7,5,5,8,7,8,8,9,9,10,10,13}; vector <int> Arg2(Arr2, Arr2 + (sizeof(Arr2) / sizeof(Arr2[0]))); int Arg3 = 50000; int Arg4 = 372837676; verify_case(6, Arg4, countPaths(Arg0, Arg1, Arg2, Arg3)); }

  // END CUT HERE


};

// BEGIN CUT HERE
int main(void) {
  HamiltonianPaths ___test;
  ___test.run_test(-1);
  system("pause");
}
// END CUT HERE
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值