[loj2091]小星星
(分别用$E_{T}$和$E_{G}$表示树和图的边集)
简单分析,可以发现题目即求排列$p_{i}$的数量,满足$\forall (x,y)\in E_{T},(p_{x},p_{y})\in E_{G}$(记为条件$A$)
定义$count(S)$为:序列$p_{i}$的数量(忽略排列的限制),满足条件$A$且$\forall 1\le i\le n,p_{i}\in S$
事实上,我们的答案即求$\sum_{S\subseteq [1,n]}(-1)^{n-|S|}f(S)$
证明可以考虑每一个满足条件$A$序列$p_{i}$的对答案的贡献:
1.若$p_{i}$是排列,显然仅有$S=[1,n]$时有贡献,且恰好为1
2.若$p_{i}$不为排列,令$T=\{a_{i}\}$其贡献即$\sum_{T\subseteq S\subseteq [1,n]}(-1)^{n-|S|}$,由于其不为排列,存在$1\le x\le n$且$x\notin T$,任取其中的一个$x$,考虑$x$是否存在不难发现两者恰好抵消,即贡献为0
综上,即仅有排列对答案有1的贡献,即得证
不妨暴力枚举$S$,考虑如何求$f(S)$:对树进行dp,用$f_{i,j}$表示以$i$为根的子树内且$p_{i}=j$的方案数,枚举儿子的值转移即可,复杂度为$o(n^{3})$
最终复杂度即$o(n^{3}2^{n})$,可以通过
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 20 4 #define ll long long 5 struct Edge{ 6 int nex,to; 7 }edge[N<<1]; 8 int E,n,m,x,y,head[N],vis[N][N]; 9 ll ans,f[N][N]; 10 void add(int x,int y){ 11 edge[E].nex=head[x]; 12 edge[E].to=y; 13 head[x]=E++; 14 } 15 void dfs(int k,int fa,int S){ 16 for(int i=0;i<n;i++) 17 if (S&(1<<i))f[k][i]=1; 18 else f[k][i]=0; 19 for(int i=head[k];i!=-1;i=edge[i].nex) 20 if (edge[i].to!=fa){ 21 dfs(edge[i].to,k,S); 22 for(int j=0;j<n;j++) 23 if (S&(1<<j)){ 24 ll s=0; 25 for(int t=0;t<n;t++) 26 if (vis[j][t])s+=f[edge[i].to][t]; 27 f[k][j]*=s; 28 } 29 } 30 } 31 int main(){ 32 scanf("%d%d",&n,&m); 33 memset(head,-1,sizeof(head)); 34 for(int i=1;i<=m;i++){ 35 scanf("%d%d",&x,&y); 36 x--,y--; 37 vis[x][y]=vis[y][x]=1; 38 } 39 for(int i=1;i<n;i++){ 40 scanf("%d%d",&x,&y); 41 x--,y--; 42 add(x,y); 43 add(y,x); 44 } 45 for(int i=0;i<(1<<n);i++){ 46 dfs(0,0,i); 47 ll s=0; 48 for(int j=0;j<n;j++)s+=f[0][j]; 49 for(int j=0;j<n;j++) 50 if (i&(1<<j))s*=-1; 51 ans+=s; 52 } 53 if (n&1)ans*=-1; 54 printf("%lld",ans); 55 }