题目地址:http://www.spoj.com/problems/IE5/
题目大意:一个n个点的无向完全图,删去m条边,求哈密尔顿圈个数。(m<=15)
算法讨论:对删去的边进行容斥,问题转化为强制经过m'条边的完全图的哈密尔顿回路。把强制经过的边缩成一个点,问题转化为求一个完全图的哈密尔顿圈个数(n-1)!/2。由于x->u->v->y和x->v->u->y是2个不同的圈,因此对答案的贡献要乘2,那么答案就是(n'-1)!*2^m'/2,其中n'表示缩点后图上点的个数,m'表示强制经过的边数。强制经过的边的端点的度要不大于2。注意一些情况的特判。
Code:
#include <cstdio>
#include <cstring>
#define M 15
#define N 300
#define mod 9901
using namespace std;
bool vis[M+10];
int T,n,m,ans,inv,u[M+10],v[M+10],fa[N+10],deg[N+10],fac[N+10],size[N+10],power[N+10];
inline int qpow(int a,int b){
int ans=1;
for (;b;b>>=1,a=a*a%mod) if (b&1) ans=ans*a%mod;
return ans;
}
inline void prepare(){
inv=qpow(2,mod-2);
fac[0]=power[0]=1;
for (int i=1;i<=N;++i) fac[i]=fac[i-1]*i%mod;
for (int i=1;i<=N;++i) power[i]=power[i-1]*2%mod;
}
int get(int x){
return x==fa[x]?x:fa[x]=get(fa[x]);
}
void dfs(int k,int cnt){
if (k>m){
memset(deg,0,sizeof(deg));
for (int i=1;i<=m;++i)
if (vis[i]){
deg[u[i]]++,deg[v[i]]++;
if (deg[u[i]]>2 || deg[v[i]]>2) return;
}
for (int i=1;i<=n;++i) fa[i]=i,size[i]=1;
for (int i=1;i<=m;++i)
if (vis[i]){
int fx=get(u[i]),fy=get(v[i]);
if (fx!=fy) fa[fx]=fy,size[fy]+=size[fx];
else if (size[fx]==n){
if (cnt&1) ans=(ans-1+mod)%mod;else ans=(ans+1)%mod;
return;
}
else return;
}
int c1=0,c2=0,t=0;
for (int i=1;i<=n;++i) c1+=get(i)==i,c2+=get(i)==i&&size[i]>1;
if (c1==1 && !c2) t=0;
else if (c1==1 && c2==1) t=1;
else if (c1==2 && !c2) t=0;
else if (c1==2 && c2==1) t=1;
else if (c1==2 && c2==2) t=2;
else if (c2) t=fac[c1-1]*power[c2-1]%mod;
else t=fac[c1-1]*inv%mod;
if (cnt&1) ans=(ans-t+mod)%mod;else ans=(ans+t)%mod;
return;
}
vis[k]=0;
dfs(k+1,cnt);
vis[k]=1;
dfs(k+1,cnt+1);
}
int main(){
scanf("%d",&T);
prepare();
for (int kase=1;kase<=T;++kase){
ans=0;
scanf("%d%d",&n,&m);
for (int i=1;i<=m;++i) scanf("%d%d",&u[i],&v[i]);
dfs(1,0);
printf("Case #%d: %d\n",kase,ans);
}
return 0;
}
By Charlie Pan
Aug 25,2014