zoukankan      html  css  js  c++  java
  • [Educational Round 17][Codeforces 762F. Tree nesting]

    题目连接:762F - Tree nesting

    题目大意:给出两个树(S,T),问(S)中有多少连通子图与(T)同构。(|S|leq 1000,|T|leq 12)

    题解:考虑树的最小表示法(有关知识可戳https://www.byvoid.com/zhs/blog/directed-tree-bracket-sequence),求出(T)以不同点为根时所有的子树状态

       开始对树(S)进行(DFS),求出每个点的状态为(t)时的方案数,由于(t)还是由(n)个数字(括号序列)合并起来的,而且(n)不会太大,所以可以用二进制DP求解

       对当前点求解时,只需遍历其儿子,将儿子的解并入当前的状态即可。由于一个点可能有若干个形状相同的子树,所以要考虑去重,具体实现见代码

    #include<bits/stdc++.h>
    using namespace std;
    #define N 1001
    #define M 1<<12
    #define MOD 1000000007
    int len(int x){return 32-__builtin_clz(x);}
    int Union(int x,int y){return (x<<len(y))|y;}
    struct Tree
    {
        int n,ans;
        int f[M][2];
        vector<int>d[N];
        map<int,int>num[N];
        map<int,vector<int> >mp;
        void read()
          {
          scanf("%d",&n);
          for(int i=2;i<=n;i++)
            {
            int u,v;
            scanf("%d%d",&u,&v);
            d[u].push_back(v);
            d[v].push_back(u);
            }
          }
        int dfs(int cur,int pre)
          {
          int res=1;
          vector<int>tmp;
          //tmp用来记录子树的状态 
          for(auto nxt:d[cur])if(nxt!=pre)
            tmp.push_back(dfs(nxt,cur));
          sort(tmp.begin(),tmp.end());
          for(auto x:tmp)res=Union(res,x);
          res<<=1;
          //res表示当前节点的最小表示 
          if(!mp.count(res))mp[res]=tmp;
          //将当前点对应的子树们也记录下来 
          return res;
          }
        void getID()
          {
          for(int i=1;i<=n;i++)
            dfs(i,0);
          //枚举以所有的点为根的情况 
          }
        void DP(int cur,int pre,const Tree &T)
          {
          for(auto nxt:d[cur])if(nxt!=pre)DP(nxt,cur,T);
          for(const auto &pi:T.mp)//枚举T中的所有状态 
            {
            auto &types=pi.second;
            int id=pi.first,n=types.size(),now=0,lst=1;
            for(int i=0;i<(1<<n);i++)f[i][0]=f[i][1]=0;
            f[0][0]=1;
            //id为当前枚举到的状态,n为当前状态拥有的子树数目,用滚动数组实现儿子们的合并 
            for(auto nxt:d[cur])if(nxt!=pre)
              {
              now^=1,lst^=1;
              for(int i=0;i<(1<<n);i++)
                f[i][now]=f[i][lst];
              for(int i=0;i<n;i++)
                if(num[nxt][types[i]])//num[i][j]表示点i的状态为j时方案有多少个 
                  for(int j=(1<<n)-1;j>=0;j--)
                    if(f[j][lst] && !((1<<i)&j) && !(i && types[i]==types[i-1] && !((1<<(i-1))&j)))
                      // (i && types[i]==types[i-1] && !((1<<(i-1))&j)代表的是
                      //当前枚举的子树和前一个子树同构 ,且前一个子树的状态未记录 
                      (f[(1<<i)|j][now]+=1ll*f[j][lst]*num[nxt][types[i]]%MOD)%=MOD;
              }
            if(f[(1<<n)-1][now])
              {
              num[cur][id]=f[(1<<n)-1][now];//集齐了所有子树即为对应num的答案 
              if(len(id)==2*T.n)(ans+=num[cur][id])%=MOD;//id的二进制长度为2m则说明一定是根节点的状态,加入答案 
              }
            }
          }
    }S,T;
    int main()
    {
        S.read();
        T.read();
        T.getID();
        S.DP(1,0,T);
        printf("%d
    ",S.ans);
    }
  • 相关阅读:
    mabatis配置文件yml配置打印sql
    java使用validator检验bean
    vue项目 老是报错 气的我就不行
    注入为空
    软件测试基础
    单元测试实战
    软件测试基础
    For循环案例---九九乘法表
    软件测试基础
    软件测试基础
  • 原文地址:https://www.cnblogs.com/DeaphetS/p/10771213.html
Copyright © 2011-2022 走看看