zoukankan      html  css  js  c++  java
  • [BJOI2015]树的同构

    https://zybuluo.com/ysner/note/1176508

    题面

    给出各种形态的树,问哪些树互为重构树?

    • (nleq50)

    解析

    (method 1)

    一开始没注意到不论树有没有根,都要以树的重心为根,根的不同可以改变树的形态,如一棵树变成一条链之类。
    树的重心的要求是使子树 最大规模 最小
    显然使用树哈希。

    [Hash[x]=sum_{异或和}(Hash[son_{1..k}]+Base1)*(sz[x]+Base2)+deep[x]*Base3 ]

    看起来这式子很容易乘爆,我们可以模一个(2^n)(自然溢出也是同一原理),以减少对 大小在模数范围以内 的二进制位的影响。
    最后再注意一下找完(root)后要重新统计子树大小
    但在(bzoj)上死都过不了,很想蒯数据

    #include<iostream>
    #include<cmath>
    #include<cstring>
    #include<cstdio>
    #include<cstdlib>
    #include<algorithm>
    #define ll long long
    #define re register
    #define il inline
    #define fp(i,a,b) for(re int i=a;i<=b;i++)
    #define fq(i,a,b) for(re int i=a;i>=b;i--)
    using namespace std;
    const int N=105,mod1=498353,mod2=412817,mod=1<<30;
    int n,h[N],cnt,m,B1=3,B2=7,B3=11,sz[N],vis1[500000],vis2[500000],dp[N],root;
    ll Hash[N];
    struct Edge{int to,next;}e[N<<1];
    il void add(re int u,re int v){e[++cnt]=(Edge){v,h[u]};h[u]=cnt;}
    il void dfs(re int u,re int fa,re int deep)
    {
      //printf("%d %d %d
    ",u,fa,deep);
      re ll sum=0;sz[u]=1;
      for(re int i=h[u];i+1;i=e[i].next)
        {
          re int v=e[i].to;
          if(v==fa) continue;
          dfs(v,u,deep+1);//printf("%d
    ",Hash[v]);
          sum^=Hash[v];
          sz[u]+=sz[v];
        }
      Hash[u]^=((sum+B1)*(sz[u]+B2)*(deep+B3));
      Hash[u]%=mod;
      //printf("%lld %d
    ",Hash[u],u);
    }
    il void getroot(re int u,re int fa)
    {
      sz[u]=1;
      for(re int i=h[u];i+1;i=e[i].next)
        {
          re int v=e[i].to;
          if(v==fa) continue;
          getroot(v,u);
          sz[u]+=sz[v];
          dp[u]=max(dp[u],sz[v]);
        }
      dp[u]=max(dp[u],n-dp[u]);
      if(dp[u]<dp[root]) root=u;
      else if(dp[u]==dp[root]&&u<root) root=u;
    }
    int main()
    {
      m=gi();
      fp(o,1,m)
        {
          memset(h,-1,sizeof(h));cnt=0;memset(Hash,0,sizeof(Hash));memset(dp,0,sizeof(dp));dp[0]=1e9;memset(sz,0,sizeof(sz));
          n=gi();root=0;
          fp(i,1,n)
        {
          re int v=gi();
          if(v) add(i,v),add(v,i);
        }
          getroot(1,0);//printf("%d %d
    ",o,root);
          dfs(root,0,1);//printf("%d %lld
    ",o,Hash[1]);
          if(vis1[Hash[root]%mod1]&&vis2[Hash[root]%mod2]) printf("%d
    ",vis1[Hash[root]%mod1]);
          else printf("%d
    ",vis1[Hash[root]%mod1]=vis2[Hash[root]%mod2]=o);
        }
      return 0;
    }
    

    (method 2)

    在对(sum[v])进行排序后,
    哈希方程变为这样((u)为当前节点,(v)为子节点,(p[i])为质数表)

    [sum[u]=sum sum[v]*p[i](uin {v}) ]

    当然当前节点也算单独一颗(size=1)的子树,要不然叶节点怎么办。。。
    因该式不考虑诸如深度、以该节点为根的子树等因素,我们就要对每个点为根的情况都进行(Hash)值计算,最后排序以后比较是否完全相同即可。

    #include<iostream>
    #include<cmath>
    #include<cstring>
    #include<cstdio>
    #include<cstdlib>
    #include<algorithm>
    #define ll long long
    #define re register
    #define il inline
    #define fp(i,a,b) for(re int i=a;i<=b;i++)
    #define fq(i,a,b) for(re int i=a;i>=b;i--)
    using namespace std;
    const int N=105;
    int n,h[N],cnt,m,p[55];
    ll Hash[N][N],dp[N];
    struct Edge{int to,next;}e[N<<1];
    il void add(re int u,re int v){e[++cnt]=(Edge){v,h[u]};h[u]=cnt;}
    il ll gi() 
    {
      re ll x=0,t=1;
      re char ch=getchar();
      while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
      if(ch=='-') t=-1,ch=getchar();
      while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
      return x*t;
    }
    il void wri(re int x)
    {
      if(x<0) putchar('-'),x=-x;
      if(x>9) wri(x/10);
      putchar(x%10+'0');
    }
    il void dfs(re int u,re int fa)
    {
      re ll top=0,s[55];s[++top]=1;
      for(re int i=h[u];i+1;i=e[i].next)
        {
          re int v=e[i].to;
          if(v==fa) continue;
          dfs(v,u);
          s[++top]=dp[v];
        }
      dp[u]=0;sort(s+1,s+1+top);
      fp(i,1,top) dp[u]+=s[i]*p[i];
    }
    il void Pre()
    {
      re int tot=0;
      fp(i,41,300)
        {
          re int flag=1;
          fp(j,2,sqrt(i)) if(i%j==0) {flag=0;break;}
          if(flag) p[++tot]=i;
          if(tot>50) break;
        }
    }
    int main()
    {
      Pre();
      m=gi();
      fp(o,1,m)
        {
          memset(h,-1,sizeof(h));cnt=0;memset(dp,0,sizeof(dp));
          n=gi();
          fp(i,1,n)
    	{
    	  re int v=gi();
    	  if(v) add(i,v),add(v,i);
    	}
          fp(i,1,n) dfs(i,0),Hash[o][i]=dp[i];
          sort(Hash[o]+1,Hash[o]+1+n);
          //fp(i,1,n) printf("%lld ",Hash[o][i]);puts("");
          fp(i,1,o)
    	{
    	  re int flag=1;
              fp(j,1,n) if(Hash[o][j]!=Hash[i][j])
    	    {
    	      //printf("%d %d %lld
    ",i,j,Hash[i][j]);
    	      flag=0;break;
    	    }
    	  if(flag) {printf("%d
    ",i);break;}
    	}
        }
      return 0;
    }
    
  • 相关阅读:
    Extjs Window用法详解 2 打印具体应用
    Extjs Window用法详解
    Ext.util.Format.date与Ext.Date.format区别, 转换时间戳
    Ext.util.Format.date 时间格式的设置与转换
    Singleton单例对象的使用
    C# Message类的属性Msg所关联的消息ID
    C#实现语音朗读功能
    iOS:二叉树多级表格的使用,使用三方库TreeTableView-master实现对json解析数据的递归遍历整理成树状结构
    iOS: 删除真机测试的Provisioning Profile后,在Code Singing中出现entitlements.plist文件无效,解决办法如下:
    iOS:Xcode7以上版本安装镜像文件.dmg
  • 原文地址:https://www.cnblogs.com/yanshannan/p/9157828.html
Copyright © 2011-2022 走看看