题解:luogu P3349([ZJOI2016] 小星星)
1. Description
小 Y 是一个心灵手巧的女孩子,她喜欢手工制作一些小饰品。她有 n 颗小星星,用 m 条彩色的细线串了起来,每条细线连着两颗小星星。
有一天她发现,她的饰品被破坏了,很多细线都被拆掉了。这个饰品只剩下了 \(n−1\) 条细线,但通过这些细线,这颗小星星还是被串在一起,也就是这些小星星通过这些细线形成了树。小 Y 找到了这个饰品的设计图纸,她想知道现在饰品中的小星星对应着原来图纸上的哪些小星星。如果现在饰品中两颗小星星有细线相连,那么要求对应的小星星原来的图纸上也有细线相连。小 Y 想知道有多少种可能的对应方式。
只有你告诉了她正确的答案,她才会把小饰品做为礼物送给你呢。
2. Solution
首先考虑一个暴力的树形 dp,\(f_{u,i,st}\) 表示 \(u\) 代表点 \(i\),\(u\) 的子树代表的点集为 \(st\) 的方案数,转移平凡,时间复杂度是 \(O(n^33^n)\),显然很难通过。
问题主要出在 \(st\) 这一维上,我们考虑通过容斥压缩掉这一维。
考虑,如果只定义 \(f_{u,i}\),则可能会出现若干个图中的点在树上没有与之对应的点,因此,我们钦定一个点集 \(st\),当我们做 dp 的时候,只允许树上的点对应 \(st\) 中的点,定义当限定的点集为 \(st\) 的时候的答案为 \(g_{st}\),则最后的答案就是 \(\sum_{st} (-1)^{n-|st|}g_{st}\),单次 dp 的时间复杂度显然是 \(O(n^3)\) 的,则最后的时间复杂度为 \(O(n^32^n)\)。
3. Code
/*by ChenMuJiu*/
/*略去缺省源与快读快写*/
int n,m,tot;
ll ans;
int cnt[1<<17],idx[20];
ll f[20][20];
bool flag[20][20];
vector<int>e[20];
void dfs(int u,int fa){
for(int i=0;i<tot;i++)
f[u][i]=1;
for(int v:e[u]){
if(v==fa)continue;
dfs(v,u);
for(int i=0;i<tot;i++){
ll sum=0;
for(int j=0;j<tot;j++)
if(flag[idx[i]][idx[j]])
sum+=f[v][j];
f[u][i]*=sum;
}
}
}
signed main(){
read(n),read(m);
for(int i=1,u,v;i<=m;i++){
read(u),read(v);
flag[u][v]=flag[v][u]=1;
}
for(int i=2,u,v;i<=n;i++){
read(u),read(v);
e[u].push_back(v);
e[v].push_back(u);
}
for(int st=0;st<(1<<n);st++){
cnt[st]=cnt[st>>1]+(st&1);
tot=0;
for(int j=0;j<n;j++)
if((st>>j)&1){
idx[tot++]=j+1;
}
dfs(1,0);
for(int j=0;j<tot;j++)
ans+=((n-tot)&1?-f[1][j]:f[1][j]);
}
write(ans),Nxt;
}

浙公网安备 33010602011771号