[bzoj4455][Zjoi2016]小星星
来自FallDream的博客,未经允许,请勿转载,谢谢。
小Y是一个心灵手巧的女孩子,她喜欢手工制作一些小饰品。她有n颗小星星,用m条彩色的细线串了起来,每条细线连着两颗小星星。有一天她发现,她的饰品被破坏了,很多细线都被拆掉了。这个饰品只剩下了n-1条细线,但通过这些细线,这颗小星星还是被串在一起,也就是这些小星星通过这些细线形成了树。小Y找到了这个饰品的设计图纸,她想知道现在饰品中的小星星对应着原来图纸上的哪些小星星。如果现在饰品中两颗小星星有细线相连,那么要求对应的小星星原来的图纸上也有细线相连。小Y想知道有多少种可能的对应方式。只有你告诉了她正确的答案,她才会把小饰品做为礼物送给你呢。
n<=17 m<=n(n-1)/2
我玩的很开心的一道题 于是决定在博客上发出来233
看到这个17想到的是上次一道题,我就猜复杂度也是2^n*n^3 然而没啥想法
然后我就打算写个暴力
f[i][j][S]表示i的子树用集合S里的数一一对应且第i个点对应j的方案数
专一的时候要枚举两个不相交的集合,于是我就打算把这个预处理出来,减少枚举无用状态的复杂度
对于每个数字,把它取反之后枚举子集塞到vector里面,然而炸空间炸了好几发 于是优化了一下只处理要用的
然后随便生成个17的数据跑的飞快,交上去居然过了... 估计是能被叉的233
说说正解,其实就是个容斥
枚举集合S,求出所有点都对应S里面的一个点,可以多个点对应一个的方案数,然后容斥一下即可。
复杂度2^n*n^3...
暴力
View Code
#include<algorithm> #include<iostream> #include<cstring> #include<cstdio> #include<vector> #define ll long long using namespace std; inline int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } ll f[17][17][1<<17]; int n,m,b[18][18],size[18],num[1<<17],ok[18][18],has[18]; vector<int> g[18],s[1<<17],a[18],q[1<<17][17]; void Pre(int x,int fa) { size[x]=1; for(int l=0;l<g[x].size();++l) if(g[x][l]!=fa) { int v=g[x][l];Pre(g[x][l],x); ok[size[x]][size[v]]=1;has[size[x]]=1; size[x]+=size[v]; } } void Dp(int x,int fa) { size[x]=1; for(int i=0;i<n;++i) f[x-1][i][1<<i]=1; for(int l=0;l<g[x].size();++l) if(g[x][l]!=fa) { int v=g[x][l];Dp(v,x); for(int J=0;J<a[size[x]].size();++J) { int j=a[size[x]][J]; for(int K=0;K<q[j][size[v]].size();++K) { int k=q[j][size[v]][K]; for(int t=0;t<s[j].size();++t) { int i=s[j][t]; for(int S=0;S<s[k].size();++S) { int I=s[k][S]; if(b[i][I]) f[x-1][i-1][j|k]+=f[x-1][i-1][j]*f[v-1][I-1][k]; } } } } size[x]+=size[v]; } } int main() { n=read();m=read(); for(int i=1;i<=m;++i) { int x=read(),y=read(); b[x][y]=b[y][x]=1; } for(int i=1;i<n;++i) { int x=read(),y=read(); g[x].push_back(y); g[y].push_back(x); } for(int i=1;i<1<<n;++i) { num[i]=num[i>>1]+(i&1); a[num[i]].push_back(i); } Pre(1,0); for(int i=1;i<1<<n;++i) { for(int j=0;j<n;++j) if(i&(1<<j)) s[i].push_back(j+1); if(!has[num[i]]) continue; int s=((1<<n)-1)^i; for(int t=s;t;t=(t-1)&s) if(ok[num[i]][num[t]]) q[i][num[t]].push_back(t); } Dp(1,0);ll ans=0; for(int i=1;i<=n;++i) ans+=f[0][i-1][(1<<n)-1]; cout<<ans; return 0; }
正解
#include<algorithm> #include<iostream> #include<cstring> #include<cstdio> #include<vector> #define ll long long using namespace std; inline int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } ll f[18][18],ans=0; int n,m,top,q[18],b[18][18],num[1<<17]; vector<int> g[18]; void Dp(int x,int fa) { for(int i=1;i<=top;++i) f[x][i]=1; for(int j=0;j<g[x].size();++j) if(g[x][j]!=fa) { Dp(g[x][j],x); for(int i=1;i<=top;++i) { ll sum=0; for(int k=1;k<=top;++k) if(b[q[i]][q[k]]) sum+=f[g[x][j]][k]; f[x][i]*=sum; } } } int main() { n=read();m=read(); for(int i=1;i<=m;++i) { int x=read(),y=read(); b[x][y]=b[y][x]=1; } for(int i=1;i<n;++i) { int x=read(),y=read(); g[x].push_back(y); g[y].push_back(x); } for(int i=1;i<1<<n;++i) { num[i]=num[i>>1]+(i&1); memset(f,0,sizeof(f));top=0; for(int t=1;t<=n;++t) if(i&(1<<t-1)) q[++top]=t; Dp(1,0);ll sum=0; for(int j=1;j<=top;++j) sum+=f[1][j]; if((n-num[i])&1)ans-=sum; else ans+=sum; } cout<<ans; return 0; }
FallDream代表秋之国向您问好!
欢迎您来我的博客www.cnblogs.com/FallDream