zoukankan      html  css  js  c++  java
  • [ZJOI2016]小星星(容斥+dp)

    洛谷链接:https://www.luogu.org/problemnew/show/P3349
    题意相当于给一棵树重新赋予彼此不同的编号,要求树上相邻的两个节点在给定的另外一个无向图中也存在边相连。
    n很小,但枚举阶乘肯定是会爆炸的。
    发现编号彼此不同对统计答案的影响太大了,我们可以尝试先让编号可以重复,但是限制可以选用的编号集,即O(2^n)枚举n个数的子集,然后容斥一下答案。
    可选用的编号集合确定了,编号还可以重复,接下来直接跑树形dp就可以了。f(u)(j)存的是u节点映射向j,子树内的总方案数。

    #include<bits/stdc++.h>
    using namespace std;
    const int N=40;
    typedef long long ll;
    #define rep(i,a,b) for(register int i=(a);i<=(b);++i)
    #define il inline
    int gr,h[N],nxt[N],to[N];
    il void tu(int x,int y){to[++gr]=y,nxt[gr]=h[x],h[x]=gr;}
    int n,m,mp[N][N],p[N],tot;
    ll ans,dp[18][18],tmp;
    void dfs(int u,int f){
        rep(j,1,tot)dp[u][j]=1;
        for(int i=h[u];i;i=nxt[i]){
            int d=to[i];
            if(d==f)continue;
            dfs(d,u);
            rep(j,1,tot){
                tmp=0;
                rep(k,1,tot){
                    if(mp[p[j]][p[k]]) tmp+=dp[d][k];
                }
                dp[u][j]*=tmp;
            }
        }
    }
    int main(){
        scanf("%d%d",&n,&m);
        int a,b;
        rep(i,1,m)scanf("%d%d",&a,&b),mp[a][b]=mp[b][a]=1;
        rep(i,1,n-1)scanf("%d%d",&a,&b),tu(a,b),tu(b,a);
        rep(j,1,(1<<n)-1){tot=0;
            rep(i,0,n-1){
                if((j>>i)&1)p[++tot]=i+1;
            }
            dfs(1,0);tmp=0;
            rep(i,1,tot)tmp+=dp[1][i];
            ans+=(((n-tot)&1)?-1ll:1ll)*tmp;
        }
        printf("%lld\n",ans);
        return 0;
    }
    
    When everthing changes,nothing changes.
  • 相关阅读:
    作业: 小型购物系统1---按顺序编写
    字典操作学习小结
    字符操作学习笔记小结
    列表,元组等学习笔记小结
    模块及其数据类型小结
    python3学习笔记----基础知识1
    压力山大
    下周一开始上班啦!
    凌晨12点,沉迷学习,无法自拔...
    web前端开发2018年12月找工作总结
  • 原文地址:https://www.cnblogs.com/Sinuok/p/10839216.html
Copyright © 2011-2022 走看看