[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...
暴力
#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;
}
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[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;
}
View Code
posted @ 2017-08-01 18:00  FallDream  阅读(384)  评论(0编辑  收藏  举报