正题
题目链接:https://www.luogu.com.cn/problem/P3349
题目大意
nnn个点的一棵树,再给出一张图,树上每个点对应图上每个点后要求树上的边图上都有,求有多少种对应方式。
解题思路
由于题目要求每个点只出现一次就加大了难度,可以考虑用容斥去掉这个限制。如果至少有kkk个点重复出现过,那么这种情况的容斥系数就为(−1)k(-1)^k(−1)k。
我们可以枚举一个集合sss,那么sss中的所有点都不选,那么就至少会有∣s∣|s|∣s∣个点重复,容斥系数就为(−1)∣s∣(-1)^{|s|}(−1)∣s∣。然后上dpdpdp即可,这样的时间复杂度大概是O(n∑s∈G∣s∣2)O(n\sum_{s\in G}|s|^2)O(n∑s∈G∣s∣2)。
计算下来大概是1e71e71e7级别的,可以通过本题。
codecodecode
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=18;
struct node{ll to,next;
}a[N*2];
ll n,m,tot,s,ans,ls[N],f[N][N];
bool v[N][N];
void addl(ll x,ll y){a[++tot].to=y;a[tot].next=ls[x];ls[x]=tot;return;
}
void dfs(ll x,ll fa){for(ll p=0;p<n;p++)if(!((1<<p)&s))f[x][p]=1;else f[x][p]=0;for(ll i=ls[x];i;i=a[i].next){ll y=a[i].to;if(y==fa)continue;dfs(y,x);for(ll p=0;p<n;p++){ll tmp=0;if(s&(1<<p))continue;for(ll q=0;q<n;q++)tmp+=f[y][q]*v[p][q];f[x][p]*=tmp;}}return;
}
int main()
{scanf("%lld%lld",&n,&m);ll MS=(1<<n);for(ll i=1;i<=m;i++){ll x,y;scanf("%lld%lld",&x,&y);x--;y--;v[x][y]=v[y][x]=1;}for(ll i=1;i<n;i++){ll x,y;scanf("%lld%lld",&x,&y);x--;y--;addl(x,y);addl(y,x);}for(s=0;s<MS;s++){dfs(0,0);ll tmp=0;for(ll i=0;i<n;i++)tmp+=f[0][i];ll w=s,z=1;while(w)w-=(w&-w),z=-z;ans+=z*tmp;}printf("%lld\n",ans);
}