您的位置:首页 > 其它

bzoj4455 ZJOI2016 小星星

2017-03-25 20:39 183 查看
直接暴力DP,用

表示以

为根的子树用图中点集

来表示的方案数,然后暴力合并,时间复杂度是

的,然后如果常数足够好是能贴着时限A的。

标算是容斥,考虑把树的点集映射到图中的点集

中,可以多个点映射到同一个点,统计方案数就非常轻易,直接DFS一波就好了。然后为了去掉多个点映射到同一个点的方案,就容斥一波就行了,时间复杂度



code:

#include<iostream>

#include<cmath>

#include<cstring>

#include<cstdio>

#include<algorithm>

using namespace std;

int B[20][20];

long long dp[20][20];

struct bian{

    int next,point;

}b[50];

int p[20],n,len,m,A[20];

void ade(int k1,int k2){

    b[++len]=(bian){p[k1],k2}; p[k1]=len;

}

void add(int k1,int k2){

    ade(k1,k2); ade(k2,k1);

}

void getw(int k1,int k2){

    for (int i=p[k1];i;i=b[i].next){

        int j=b[i].point;

        if (j!=k2) getw(j,k1);

    }

    for (int now=1;now<=m;now++){

        dp[k1][now]=1;

        for (int i=p[k1];i;i=b[i].next){

            int j=b[i].point; long long ans=0;

            if (j!=k2){

                for (int k=1;k<=m;k++)

                    if (B[A[now]][A[k]]) ans+=dp[j][k];

                dp[k1][now]*=ans;

            }

        }

    }

}

int main(){

    freopen("star.in","r",stdin);

    freopen("star.out","w",stdout);

    scanf("%d%d",&n,&m);

    for (int i=1;i<=m;i++){

        int k1,k2; scanf("%d%d",&k1,&k2); B[k1][k2]=1; B[k2][k1]=1;

    }

    for (int i=1;i<n;i++){

        int k1,k2; scanf("%d%d",&k1,&k2); add(k1,k2);

    }

    long long ans=0;

    for (int i=0;i<(1<<n);i++){

        int num=n; m=0;

        for (int j=0;j<n;j++) if (i&(1<<j)) num--,A[++m]=j+1;

        long long w=0; getw(1,0);

        for (int i=1;i<=m;i++) w+=dp[1][i];

        if (num&1) ans-=w; else ans+=w;

    }

    cout<<ans<<endl; return 0;

}

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