传送门:bzoj5006
题解
状压 S S S表示左右部分别选了哪些点构成完美匹配, m a p map map记忆化 f [ S ] f[S] f[S]表示 S S S点集完美匹配的方案数 × 2 ∣ S ∣ 2 \times 2^{\frac{|S|}{2}} ×22∣S∣(显然 ∣ S ∣ |S| ∣S∣为偶数)。
为了减少状态数并避免重复计算,枚举左部中 l o w b i t lowbit lowbit连向的右部的点:
对于0的情况,转移系数 2 × 50 % = 1 2\times 50\%=1 2×50%=1,直接转移即可。
对于1,2的情况,首先将两条边分别看做0的情况直接转移;仅当两条边同时选入完美匹配时概率不对,那么判断 S S S中是否可以同时选这两条边,强制选另一条边进行转移,系数 2 × 2 × 25 % = 1 2\times 2\times 25\%=1 2×2×25%=1,1的情况加上,2的情况减去即可。
复杂度 O ( 能 过 ) O(能过) O(能过)
代码
#include<bits/stdc++.h>
#define rc(a,b) (((a)<<15)|(b))
using namespace std;
typedef long long ll;
const int N=(1<<15)+10,mod=1e9+7,M=15;
int n,m,typ[M][M],s[N],S;
struct P{
int tp;
int ax,ay,bx,by;
}p[300];
map<int,int>f;
inline void ad(int &x,int y){x+=y;if(x>=mod) x-=mod;}
inline void dc(int &x,int y){x-=y;if(x<0) x+=mod;}
int cal(int a,int b)
{
if(f.find(rc(a,b))!=f.end()) return f[rc(a,b)];
int i,x=a&(-a),y,t=b,re=0,c,d;
for(y=t&(-t);t;y=t&(-t)){
t^=y;
if((i=typ[s[x]][s[y]])){
ad(re,cal(a^x,b^y));
if(!p[i].tp) continue;
((x==p[i].ax)&&(y==p[i].ay))?(c=p[i].bx,d=p[i].by):(c=p[i].ax,d=p[i].ay);
if((c!=x)&&(d!=y)&&(a&c)&&(b&d)){
d=cal(a^x^c,b^y^d);
(p[i].tp==1)?ad(re,d):dc(re,d);
}
}
}
f[rc(a,b)]=re;
return re;
}
int main(){
int i,j,x,y;
scanf("%d%d",&n,&m);S=(1<<n)-1;
for(i=1;i<=m;++i){
scanf("%d",&p[i].tp);
scanf("%d%d",&x,&y);x--;y--;
typ[x][y]=i;
if(p[i].tp){
p[i].ax=1<<x;p[i].ay=1<<y;
scanf("%d%d",&x,&y);x--;y--;
typ[x][y]=i;p[i].bx=1<<x;p[i].by=1<<y;
}
}
for(i=0;i<n;++i) s[1<<i]=i;f[0]=1;
printf("%d",cal(S,S));
return 0;
}