bzoj4455 ZJOI2016 小星星

直接暴力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;
}
        

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值