您的位置:首页 > 其它

bzoj4455 uoj185 ZJOI2016T3 小星星 树形dp&容斥

2016-03-25 14:02 337 查看
考场上对这道题目一点思路都没有。。一直以为状态和顺序有关然后就gg了QAQ。然而这就是能用子集dp的!/(ㄒoㄒ)/~~都想到用容斥了

。。但是dp状态搞不出来QAQ。

令f[i][j][S]表示以i所在的子树(不妨令1为根节点)中与图中编号集合为S的点一一对应,且i与j对应的时候的方案数,然后就可以大力转移了。这样是O(N^3*3^N)的,拿的分好像和暴力差不多

。(听说可以用f[i][S]表示状态然后大力转移为O(N3^N)但是窝不会QAQ)

考虑容斥,枚举T表示所有点和图中集合为T的点对应的情况(不保证一一对应),然后答案就是和U(全集)一一对应的情况,容斥一下就好了。枚举是O(2^N)的,然后树形dp为O(N^3),因此时间复杂度为O(N^3*2^N),这样就可以过了。(在uoj上面卡了一下常数就过了)。

AC代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;

int n,m,cnt,tot,fst[21],pnt[42],nxt[42],bin[21],a[21]; bool mp[21][21]; ll dp[21][21];
int read(){
int x=0; char ch=getchar();
while (ch<'0' || ch>'9') ch=getchar();
while (ch>='0' && ch<='9'){ x=x*10+ch-'0'; ch=getchar(); }
return x;
}
void add(int x,int y){
pnt[++tot]=y; nxt[tot]=fst[x]; fst[x]=tot;
}
void dfs(int x,int fa){
int p,i,j,y; ll tmp;
for (p=fst[x]; p; p=nxt[p])
if (pnt[p]!=fa) dfs(pnt[p],x);
for (i=1; i<=cnt; i++){
dp[x][i]=1;
for (p=fst[x]; p; p=nxt[p]){
tmp=0; y=pnt[p]; if (y==fa) continue;
for (j=1; j<=cnt; j++)
if (mp[a[i]][a[j]]) tmp+=dp[y][j];
dp[x][i]*=tmp; if (!dp[x][i]) break;
}
}
}
int main(){
n=read(); m=read(); int i,x,y; ll ans=0,sum;
bin[0]=1; for (i=1; i<=n; i++) bin[i]=bin[i-1]<<1;
for (i=1; i<=m; i++){
x=read(); y=read();
mp[x][y]=mp[y][x]=1;
}
for (i=1; i<n; i++){
x=read(); y=read(); add(x,y); add(y,x);
}
for (i=1; i<bin
; i++){
sum=cnt=0;
for (x=1; x<=n; x++) if (i&bin[x-1]) a[++cnt]=x;
dfs(1,0);
for (x=1; x<=cnt; x++) sum+=dp[1][x];
if ((n^cnt)&1) ans-=sum; else ans+=sum;
}
printf("%lld\n",ans);
return 0;
}


by lych
2016.3.25
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: