[容斥 & 状压DP & FFT] Tco 2016 Final. HamiltonianPaths

22 篇文章 0 订阅
22 篇文章 1 订阅

相当于是问有多少种排列,使得相邻的点之间没有边
考虑容斥
一张图中选了 d 条边,那么会形成 nd 条链,设所有图中的链总共有 x 条,那么答案乘上 x!
只要DP出形成 a <script type="math/tex" id="MathJax-Element-128">a</script> 条链的方案数,然后NTT一下就可以了

// BEGIN CUT HERE  

// END CUT HERE  
#include <vector>  
#include <list>  
#include <map>  
#include <set>  
#include <deque>  
#include <stack>  
#include <bitset>  
#include <algorithm>  
#include <functional>  
#include <numeric>  
#include <utility>  
#include <sstream>  
#include <iostream>  
#include <iomanip>  
#include <cstdio>  
#include <cmath>  
#include <cstdlib>  
#include <ctime>  
#include <queue>
#include <assert.h>
#include <cstring>  

using namespace std;  

const int N=20,P=998244353;

int n,k,m,cnt,G[N];
struct edge{
  int t,nx;
}E[N*N<<1];

inline void addedge(int x,int y){
  E[++cnt].t=y; E[cnt].nx=G[x]; G[x]=cnt;
  E[++cnt].t=x; E[cnt].nx=G[y]; G[y]=cnt;
}

int f[1<<14|5][N],g[1<<14|5][N],h[1<<14|5];

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

const int NN=6000010;

int w[2][NN],num,rev[NN],F[NN];

inline int Pow(int x,int y){
  int ret=1;
  for(;y;y>>=1,x=1LL*x*x%P) if(y&1) ret=1LL*ret*x%P;
  return ret;
}

inline void Pre(int n){
  num=n; int g=Pow(3,(P-1)/num);
  w[0][0]=w[1][0]=1;
  for(int i=1;i<num;i++) w[1][i]=1LL*w[1][i-1]*g%P;
  for(int i=1;i<num;i++) w[0][i]=w[1][num-i];
}

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

class HamiltonianPaths{  
public:  
  int countPaths(int n, vector <int> a, vector <int> b, int k){
    for(int i=0;i<a.size();i++)
      addedge(a[i]+1,b[i]+1);
    for(int i=1;i<=n;i++) f[1<<(i-1)][i]=1;
    for(int S=1;S<(1<<n);S++)
      for(int x=1;x<=n;x++){
    if(!f[S][x]) continue;
    for(int i=G[x];i;i=E[i].nx)
      if(S>>(E[i].t-1)&1);else
        add(f[S|(1<<(E[i].t-1))][E[i].t],f[S][x]);
    add(h[S],f[S][x]);
      }
    g[0][0]=1;
    for(int S=1;S<(1<<n);S++)
      for(int i=1;i<=n;i++)
    for(int s=S;s;s=(s-1)&S)
      add(g[S][i],1LL*g[S^s][i-1]*h[s]%P);
    int L=0,M; for(M=1;M<=n*k;M<<=1,L++); Pre(M<<=1);
    for(int i=1;i<num;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<L);
    for(int i=1,fac=1;i<=n;fac=1LL*fac*(++i)%P)
      F[i]=1LL*g[(1<<n)-1][i]*Pow(fac,P-2)%P;
    NTT(F,num,1);
    for(int i=0;i<num;i++) F[i]=Pow(F[i],k);
    NTT(F,num,0); int ans=0;
    for(int i=1,fac=1;i<=n*k;fac=1LL*fac*(++i)%P)
      if((n*k-i)&1)
    ans=(ans-1LL*fac*F[i])%P;
      else
    ans=(ans+1LL*fac*F[i])%P;
    return (ans+P)%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()  
{  
  HamiltonianPaths ___test;  
  ___test.run_test(0);  
  system("pause");  
}  
// END CUT HERE  
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值