[哈密顿路径 FFT 容斥] TCO 2016 Final HamiltonianPaths

只能走补图的边
那么原图色的边不能走
那么我们容斥走了几条原图的边
这些边肯定是一些链
把链缩成一个点 那么不考虑其他不合法边用不用的情况下 贡献是 m的阶乘
m是缩完够的点数
然后 因为缩起来的点只可能是在同一个模板图中
那么对于最后总点数m的缩点方案数 是可以先处理出单个模板图的方案数 然后fft的
状压dp预处理模板图

// BEGIN CUT HERE  
#include<conio.h>
#include<sstream>
// END CUT HERE  
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
#include<string>
#include<set>
#define cl(x) memset(x,0,sizeof(x))
using namespace std;
typedef long long ll;

const int N=1200005;
const int P=998244353;

inline ll Pow(ll a,int b){
  ll ret=1;
  for (;b;b>>=1,a=a*a%P)
    if (b&1)
      ret=ret*a%P;
  return ret;
}

int num;
int w[2][N];
int R[N];

inline void Pre(int m){
  num=m; ll g=Pow(3,(P-1)/m);
  w[0][0]=w[1][0]=1;
  for (int i=1;i<m;i++) w[1][i]=w[1][i-1]*g%P;
  for (int i=1;i<m;i++) w[0][i]=w[1][m-i];
  int L=0,t=m; while (t>>=1) L++;
  for (int i=1;i<m;i++) R[i]=(R[i>>1]>>1)|((i&1)<<(L-1));
}

inline void FFT(int *a,int n,int r){
  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++){
    int x=a[j+k],y=(ll)a[j+i+k]*w[r][num/(i<<1)*k]%P;
    a[j+k]=(x+y)%P,a[j+i+k]=(x+P-y)%P;
      }
  if (!r) for (int i=0,inv=Pow(n,P-2);i<n;i++) a[i]=(ll)a[i]*inv%P;
}

const int KK=14;

struct edge{
  int u,v,next;
}G[KK*KK+5];
#define V G[p].v
int head[KK+5],inum;
inline void add(int u,int v,int p){
  G[p].u=u; G[p].v=v; G[p].next=head[u]; head[u]=p;
}

int n,m,K;
int f[1<<KK][KK+5],F[1<<KK];
int g[1<<KK][KK+5];

inline void add(int &x,int y){
  x+=y; if (x>=P) x-=P;
}

int a[N];

class HamiltonianPaths{
public:
  int countPaths(int _k, vector <int> _a, vector <int> _b, int _n){
    K=_k; n=_n; m=_a.size();
    for (int i=1;i<=m;i++) add(_a[i-1]+1,_b[i-1]+1,++inum),add(_b[i-1]+1,_a[i-1]+1,++inum);
    for (int i=1;i<=K;i++) f[1<<(i-1)][i]=1;
    for (int i=1;i<(1<<K);i++)
      for (int j=1;j<=K;j++)
    if ((i>>(j-1)&1) && f[i][j]){
      for (int p=head[j];p;p=G[p].next)
        if (!(i>>(V-1)&1))
          add(f[i|(1<<(V-1))][V],f[i][j]);
    }
    for (int i=1;i<(1<<K);i++)
      for (int j=1;j<=K;j++)
    add(F[i],f[i][j]);
    g[0][0]=1;
    for (int i=1;i<(1<<K);i++)
      for (int j=1;j<=K;j++)
    for (int k=i;k;k=(--k)&i)
      add(g[i][j],(ll)g[i^k][j-1]*F[k]%P);
    int m=1; while (m<=n*K) m<<=1; Pre(m);
    ll fac=1;
    for (int i=1;i<=K;i++,fac=fac*i%P)
      a[i]=(ll)g[(1<<K)-1][i]*Pow(fac,P-2)%P;
    FFT(a,m,1);
    for (int i=0;i<m;i++) a[i]=Pow(a[i],n);
    FFT(a,m,0);
    ll ans=0; fac=1;
    for (int i=1;i<=n*K;i++,fac=fac*i%P)
      if ((n*K-i)&1)
    ans+=P-fac*a[i]%P;
      else
    ans+=fac*a[i]%P;
    return ans%P;
  }


  // 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(int argc,char* argv[]){
  HamiltonianPaths ___test; int tt;
  scanf("%d",&tt);
  ___test.run_test(tt);
  getch() ;
  return 0;
}
// END CUT HERE
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值