• [loj2091]小星星


    (分别用$E_{T}$和$E_{G}$表示树和图的边集)

    简单分析,可以发现题目即求排列$p_{i}$的数量,满足$forall (x,y)in E_{T},(p_{x},p_{y})in E_{G}$(记为条件$A$)

    定义$count(S)$为:序列$p_{i}$的数量(忽略排列的限制),满足条件$A$且$forall 1le ile n,p_{i}in S$

    事实上,我们的答案即求$sum_{Ssubseteq [1,n]}(-1)^{n-|S|}f(S)$

    证明可以考虑每一个满足条件$A$序列$p_{i}$的对答案的贡献:

    1.若$p_{i}$是排列,显然仅有$S=[1,n]$时有贡献,且恰好为1

    2.若$p_{i}$不为排列,令$T={a_{i}}$其贡献即$sum_{Tsubseteq Ssubseteq [1,n]}(-1)^{n-|S|}$,由于其不为排列,存在$1le xle n$且$x otin T$,任取其中的一个$x$,考虑$x$是否存在不难发现两者恰好抵消,即贡献为0

    综上,即仅有排列对答案有1的贡献,即得证

    不妨暴力枚举$S$,考虑如何求$f(S)$:对树进行dp,用$f_{i,j}$表示以$i$为根的子树内且$p_{i}=j$的方案数,枚举儿子的值转移即可,复杂度为$o(n^{3})$

    最终复杂度即$o(n^{3}2^{n})$,可以通过

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define N 20
     4 #define ll long long
     5 struct Edge{
     6     int nex,to;
     7 }edge[N<<1];
     8 int E,n,m,x,y,head[N],vis[N][N];
     9 ll ans,f[N][N];
    10 void add(int x,int y){
    11     edge[E].nex=head[x];
    12     edge[E].to=y;
    13     head[x]=E++;
    14 }
    15 void dfs(int k,int fa,int S){
    16     for(int i=0;i<n;i++)
    17         if (S&(1<<i))f[k][i]=1;
    18         else f[k][i]=0;
    19     for(int i=head[k];i!=-1;i=edge[i].nex)
    20         if (edge[i].to!=fa){
    21             dfs(edge[i].to,k,S);
    22             for(int j=0;j<n;j++)
    23                 if (S&(1<<j)){
    24                     ll s=0;
    25                     for(int t=0;t<n;t++)
    26                         if (vis[j][t])s+=f[edge[i].to][t];
    27                     f[k][j]*=s;
    28                 }
    29         }
    30 }
    31 int main(){
    32     scanf("%d%d",&n,&m);
    33     memset(head,-1,sizeof(head));
    34     for(int i=1;i<=m;i++){
    35         scanf("%d%d",&x,&y);
    36         x--,y--;
    37         vis[x][y]=vis[y][x]=1;
    38     }
    39     for(int i=1;i<n;i++){
    40         scanf("%d%d",&x,&y);
    41         x--,y--;
    42         add(x,y);
    43         add(y,x);
    44     }
    45     for(int i=0;i<(1<<n);i++){
    46         dfs(0,0,i);
    47         ll s=0;
    48         for(int j=0;j<n;j++)s+=f[0][j];
    49         for(int j=0;j<n;j++)
    50             if (i&(1<<j))s*=-1;
    51         ans+=s;
    52     }
    53     if (n&1)ans*=-1;
    54     printf("%lld",ans);
    55 }
    View Code
  • 相关阅读:
    Chrome 常用快捷键
    Java SE基础部分——常用类库之Math和Random类(随机产生数值)
    JavaWeb知识回顾-使用IDEA开发一个servlet.
    JavaWeb知识回顾-Servlet常用类、接口及其方法
    JavaWeb知识回顾-servlet生命周期。
    JavaWeb知识回顾-servlet简介。
    (转)Oracle 获取上周一到周末日期的查询sql语句
    mybatis 批量更新
    (转)Ehcache 整合Spring 使用页面、对象缓存
    java设计模式:单例模式
  • 原文地址:https://www.cnblogs.com/PYWBKTDA/p/14596765.html
Copyright © 2020-2023  润新知