• 【bzoj 4455】小星星(树型DP+容斥原理+dfs建树和计算的2种方式)


    题意:给一个n个点的图和一个n个点的树,求图和树上的点一一对应的方案数。(N<=17)

    解法:
    1.在树的结构上进行tree DP,f[i][j]表示树上点 i 对应图上点 j 时,这个点所在子树的方案数。O(n^3)。

    2.我们可以发现如果按这个定义进行DP,“一 一对应”的关系挺难保证。若枚举出全排列得到对应关系,这样就C(n,n)=n! 只能拿到暴力分;那么我们就不限制“一 一对应”而改为“一对多”的关系进行tree DP,利用容斥原理达到O(2^n)的复杂度。(P.S.至于为什么用容斥原理我也不清楚,待我弄懂之后我会再更新的。    2个月后的今天 我说:“应该不会有更新了......”≡[。。]≡)

    3.这题的容斥原理应用是这样的:用二进制数枚举出每次DP有哪些数没有对应的树上的点,将所有情况下的DP方案数之和按求补集的公式来求就是“所有数都一一对应树上的点”的答案。

    下图中圆圈1表示数1没有对应的点的方案数,依次类推。有颜色部分是我们要求的补集。

    下面附上代码——

      1 #include<cstdio>
      2 #include<cstdlib>
      3 #include<cstring>
      4 #include<iostream>
      5 using namespace std;
      6 
      7 typedef long long LL;
      8 const int N=20,M=400;
      9 struct node{int x,y,next;}a[2*N];
     10 int last[N],len;
     11 bool v[N][N],vis[N];
     12 LL f[N][N];
     13 int b[N],bt;
     14 
     15 void add(int x,int y)
     16 {
     17     len++;
     18     a[len].x=x,a[len].y=y;
     19     a[len].next=last[x],last[x]=len;
     20 }
     21 
     22 void dfs(int x,int fa)
     23 {
     24     /*for(int k=last[x];k;k=a[k].next)
     25     {
     26       int y=a[k].y;
     27       if(y==fa)continue;
     28       dfs(y,x);
     29     }
     30     for (int kk=1;kk<=bt;kk++)
     31     {
     32       int i=b[kk];
     33       f[x][i]=1;
     34       for (int k=last[x];k;k=a[k].next)
     35       {
     36         int y=a[k].y;
     37         if (y==fa) continue;
     38         LL h=0;
     39         for (int kkk=1;kkk<=bt;kkk++)
     40         {
     41           int j=b[kkk];
     42           if (v[i][j]) h+=f[y][j];
     43         }
     44         f[x][i]*=h;
     45       }
     46     }*///边建树,边不重复地DP
     47     if (vis[x]) return;
     48     for (int kk=1;kk<=bt;kk++)
     49     {
     50       int i=b[kk];
     51       f[x][i]=1;
     52       for (int k=last[x];k;k=a[k].next)
     53       {
     54         int y=a[k].y;
     55         if (y==fa) continue;
     56         dfs(y,x);
     57         vis[y]=true;
     58         LL h=0;
     59         for (int kkk=1;kkk<=bt;kkk++)
     60         {
     61           int j=b[kkk];
     62           if (v[i][j]) h+=f[y][j];
     63         }
     64         f[x][i]*=h;
     65       }
     66     }//打标记,快一点
     67 }
     68 
     69 int main()
     70 {
     71     int n,m;
     72     scanf("%d%d",&n,&m);
     73     memset(v,false,sizeof(v));
     74     for (int i=1;i<=m;i++)
     75     {
     76       int x,y;
     77       scanf("%d%d",&x,&y);
     78       v[x][y]=v[y][x]=true;
     79     }
     80     memset(last,0,sizeof(last));
     81     len=0;
     82     for (int i=1;i<n;i++)
     83     {
     84       int x,y;
     85       scanf("%d%d",&x,&y);
     86       add(x,y),add(y,x);
     87     }
     88     LL ans=0;
     89     for (int i=1;i<(1<<n);i++)
     90     {
     91       bt=0;
     92       for (int j=1;j<=n;j++)
     93         if (i&(1<<(j-1))) b[++bt]=j;
     94       memset(vis,false,sizeof(vis));
     95       dfs(1,0);
     96       LL h=0;
     97       for (int j=1;j<=bt;j++)
     98         h+=f[1][b[j]];
     99       if ((n-bt)%2==0) ans+=h;//按补集
    100       else ans-=h;
    101     }
    102     printf("%lld
    ",ans);
    103     return 0;
    104 }
  • 相关阅读:
    Comet OJ
    Comet OJ
    Comet OJ
    Comet OJ
    Codeforces Round #562 (Div. 2)
    P1202 USACO1.1 黑色星期五
    P1201 USACO1.1 贪婪的送礼者
    【线段树】HDU1166:敌兵布阵
    标准C++中的string类的用法总结(转)
    【递归】分形
  • 原文地址:https://www.cnblogs.com/konjak/p/5876427.html
Copyright © 2020-2023  润新知