【题解】Luogu P3349 [ZJOI2016]小星星

原题传送门

我们考虑设\(dp_{i,j}\)表示树上的点\(i\)在图上对应的点为\(j\)\(i\)和子树对应在图上的方案数

\(dp_{u_i}=\prod_{v \in u.son} dp_{v,j}*w[i][j]\)(w[i][j]表示在图中\(i\)\(j\)是否连通)

这个dp的复杂度是\(O(n^3)\),但是会发现它会多算答案,因为这个算法有可能会将多个树上的点对应到一个图上的点

我们珂以二进制枚举,枚举哪些图上的点参与dp,按照容斥原理加减,这样就珂以算出答案,复杂度为\(O(2^n n^3)\)

#include <bits/stdc++.h>
#define ll long long
#define N 20
#define getchar nc
using namespace std;
inline char nc(){
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int read()
{
    register int x=0,f=1;register char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return x*f;
}
inline void write(register ll x)
{
    if(!x)putchar('0');if(x<0)x=-x,putchar('-');
    static int sta[20];register int tot=0;
    while(x)sta[tot++]=x%10,x/=10;
    while(tot)putchar(sta[--tot]+48);
}
struct edge{
    int to,next;
}e[N<<1];
int head[N],cnt=0;
inline void add(register int u,register int v)
{
    e[++cnt]=(edge){v,head[u]};
    head[u]=cnt;
}
int n,m,w[N][N],ban[N],vis[N];
ll f[N][N],ans;
inline void dfs(register int x)
{
    vis[x]=1;
    for(register int i=1;i<=n;++i)
        f[x][i]=1;
    for(register int i=head[x];i;i=e[i].next)
    {
        int v=e[i].to;
        if(vis[v])
            continue;
        dfs(v);
        for(register int j=1;j<=n;++j)
        {
            ll sum=0;
            for(register int k=1;k<=n;++k)
                sum+=f[v][k]*(w[k][j]&ban[k]&ban[j]);
            f[x][j]*=sum;
        }
    }
}
int main()
{
    n=read(),m=read();
    for(register int i=1;i<=m;++i)
    {
        int u=read(),v=read();
        w[u][v]=w[v][u]=1;
    }
    for(register int i=1;i<n;++i)
    {
        int u=read(),v=read();
        add(u,v),add(v,u);
    }
    for(register int k=1,siz;k<=(1<<n)-1;++k)
    {
        siz=n;
        for(register int i=1;i<=n;++i)
            ban[i]=0;
        for(register int i=1,p=k;p;p>>=1,++i)
            ban[i]=p&1,siz-=p&1;
        for(register int i=1;i<=n;++i)
            vis[i]=0;
        dfs(1);
        ll cnt=0;
        for(register int i=1;i<=n;++i)
            cnt+=f[1][i];
        if(siz%2)
            ans-=cnt;
        else
            ans+=cnt;
    }
    write(ans);
	return 0;
}
posted @ 2019-07-29 10:24  JSOI爆零珂学家yzhang  阅读(273)  评论(0编辑  收藏  举报