传送门:bzoj4455
题解
此题dp姿势清奇,反正本蒟蒻没有想出来。
考虑到如果强制用状压对应每种状态很不好转移。
所以我们可以强制每次不选某些点,且在可选的点里可以重复选点的情况。
容斥一下即可。
具体看代码?
代码
#include<cstdio>
#include<cctype>
using namespace std;
typedef long long ll;
int n,m,mp[20][20],a[20],bin[20],cnt;
int head[20],to[40],nxt[40],tot;
ll f[20][20],sum,ans;
#ifdef ONLINE_JUDGE
char *TT,*mo,but[(1<<15)+2];
#define getchar() ((TT==mo&&(mo=(TT=but)+fread(but,1,1<<15,stdin)),TT==mo)?0:*TT++)
#endif
inline int rd()
{
register char ch=getchar();register int x=0,f=1;
while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+(ch^48);ch=getchar();}
return x*f;
}
inline void lk(const int u,const int v)
{to[++tot]=v;nxt[tot]=head[u];head[u]=tot;}
inline void cal(int x,int fa)
{
register int i,j,k;ll w;
for(i=head[x];i;i=nxt[i])
if(to[i]!=fa)
cal(to[i],x);
for(k=1;k<=cnt;++k){
f[x][k]=1;
for(i=head[x];i;i=nxt[i]){
if(to[i]==fa) continue;w=0;
for(j=1;j<=cnt;++j)
if(mp[a[k]][a[j]]) w+=f[to[i]][j];
f[x][k]*=w; if(!f[x][k]) break;//一定要break
}
}
}
int main(){
register int i,j,k;
bin[0]=1;for(i=1;i<=17;++i) bin[i]=bin[i-1]<<1;
n=rd();m=rd();
for(i=1;i<=m;++i){j=rd();k=rd();mp[j][k]=mp[k][j]=1;}
for(i=1;i<n;++i){j=rd();k=rd();lk(j,k);lk(k,j);}
for(register int s=1;s<bin[n];++s){
sum=0;cnt=0;for(i=1;i<=n;++i) if(s&bin[i-1]) a[++cnt]=i;
cal(1,0);for(i=1;i<=cnt;++i) sum+=f[1][i];
ans+=(cnt^n)&1? -sum:sum;
}
printf("%lld\n",ans);
}