codeforces 690F2 Tree of Life (medium) 树hash

题意:一棵树,分别给出去掉第i个点之后剩下的图,点的顺序打乱,图的顺序打乱,求这棵树原来长什么样。 n<=100

一定有一个图是去掉一个叶子节点之后的,找到这个图,枚举叶子加到哪,然后对于加完叶子的树枚举删掉一个点,把删完点的图hash出来扔到multiset里,再看其他图hash出来的值和这个multiset里的东西一不一样就行了。

无根树hash需要先找到重心,从重心hash,如果有两个重心还要在之间多加一个点。hash时将叶子的hash值排个序,hash出来就行了。

哦,顺便一提,我的是最短代码。

upd 广告:觉得这道题太水了么? 觉得树hash仅此而已了么 ?
请看下篇题解codeforces 690F3 Tree of Life (hard)。

spoiler alert : 对于下一道题可能引起的不适反应,请自备纸带。

#include <bits/stdc++.h>
using namespace std;
#define N 110
#define ull unsigned long long
#define seed 11333333
int n,T;
multiset<ull>se;
multiset<ull>::iterator it;
ull val[N];
struct tree
{
    int m,top,f1,f2,sum,id;
    int vis[N],size[N],used[N],f[N];
    vector<int>v[N];
    vector<ull>v1[N];
    ull st[N];

    void dfs1(int x,int y)
    {
        size[x]=1;used[x]=1;
        for(int i=0,t;i<v[x].size();i++)
            if((t=v[x][i])!=y&&!vis[t])
                dfs1(t,x),size[x]+=size[t];
    }
    void dfs2(int x,int y)
    {
        f[x]=sum-size[x];
        for(int i=0,t;i<v[x].size();i++)
            if((t=v[x][i])!=y&&!vis[t])
                dfs2(t,x),f[x]=max(f[x],size[t]);
        if(f[x]<f[f1])f1=x;
        else if(f[x]<f[f2])f2=x;    
    }
    ull dfs3(int x,int y)
    {
        v1[x].clear();
        for(int i=0,t;i<v[x].size();i++)
            if((t=v[x][i])!=y&&!vis[t])
                v1[x].push_back(dfs3(t,x));
        sort(v1[x].begin(),v1[x].end());
        ull ret=1;
        for(int i=0;i<v1[x].size();i++)
            ret=ret*seed+v1[x][i];
        return ret;
    }
    ull cal(int x)
    {
        f1=f2=0;f[0]=1<<30;
        dfs1(x,0);
        sum=size[x];dfs2(x,0);
        if(f[f1]!=f[f2])
            return dfs3(f1,0);
        ull t1=dfs3(f1,f2);
        ull t2=dfs3(f2,f1);
        if(t1>t2)swap(t1,t2);
        return t1*seed+t2;
    }
    void init(int x)
    {
        scanf("%d",&m);id=x;
        for(int i=1;i<=n;i++)v[i].clear();
        for(int i=1,x,y;i<=m;i++)
        {
            scanf("%d%d",&x,&y);
            v[x].push_back(y);
            v[y].push_back(x);
        }
        memset(used,0,sizeof(used));
        top=0;
        for(int i=1;i<=n;i++)
            if(!used[i])
                st[++top]=cal(i);
        sort(st+1,st+1+top);val[id]=0;
        for(int i=2;i<=top;i++)
            val[id]=val[id]*seed+st[i];
    }
    int check()
    {
        se.clear();
        for(int i=1;i<=n;i++)
        {
            vis[i]=1;top=0;
            for(int j=0;j<v[i].size();j++)
                st[++top]=cal(v[i][j]);
            vis[i]=0;ull tmp=0;
            sort(st+1,st+1+top);
            for(int j=1;j<=top;j++)
                tmp=tmp*seed+st[j];
            se.insert(tmp);
        }
        for(int i=1;i<=n;i++)
            if(i!=id)
            {
                if((it=se.find(val[i]))==se.end())
                    return 0;
                se.erase(it);
            }
        return 1;
    }
    void print()
    {
        puts("YES");
        for(int i=1;i<=n;i++)
        {
            for(int j=0,t;j<v[i].size();j++)
                if((t=v[i][j])<i)
                    printf("%d %d\n",t,i);
        }
    }
}tr[N];
void solve()
{
    scanf("%d%*d",&n);
    for(int i=1;i<=n;i++)
        tr[i].init(i);
        for(int i=1;i<=n;i++)
        if(tr[i].top==2)
        {
            for(int j=1;j<=n;j++)
                if(tr[i].v[j].empty())
                {
                    for(int k=1;k<=n;k++)
                        if(k!=j)
                        {
                            tr[i].v[k].push_back(j);
                            tr[i].v[j].push_back(k);
                            if(tr[i].check())
                            {
                                tr[i].print();
                                return;
                            }
                            tr[i].v[k].pop_back();
                            tr[i].v[j].pop_back();
                        }
                }
            break;
        }
    puts("NO"); 
}
int main()
{
    //freopen("tt.in","r",stdin);
    scanf("%d",&T);
    while(T--)
    {
        solve();
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值