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;
}
        

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章