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;
}
下载前必看:https://pan.quark.cn/s/a4b39357ea24 在本资料中,将阐述如何运用JavaScript达成单击下拉列表框选定选项后即时转向对应页面的功能。 此种技术适用于网页布局中用户需迅速选取并转向不同页面的情形,诸如网站导航栏或内容目录等场景。 达成此功能,能够显著改善用户交互体验,精简用户的操作流程。 我们须熟悉HTML里的`<select>`组件,该组件用于构建一个选择列表。 用户可从中选定一项,并可引发一个事件来响应用户的这一选择动作。 在本次实例中,我们借助`onchange`事件监听器来实现当用户在下拉列表框中选定某个选项时,页面能自动转向该选项关联的链接地址。 JavaScript里的`window.location`属性旨在获取或设定浏览器当前载入页面的网址,通过变更该属性的值,能够实现页面的转向。 在本次实例的实现方案里,运用了`eval()`函数来动态执行字符串表达式,这在现代的JavaScript开发实践中通常不被推荐使用,因为它可能诱发安全问题及难以排错的错误。 然而,为了本例的简化展示,我们暂时搁置这一问题,因为在更复杂的实际应用中,可选用其他方法,例如ES6中的模板字符串或其他函数来安全地构建和执行字符串。 具体到本例的代码实现,`MM_jumpMenu`函数负责处理转向逻辑。 它接收三个参数:`targ`、`selObj`和`restore`。 其中`targ`代表要转向的页面,`selObj`是触发事件的下拉列表框对象,`restore`是标志位,用以指示是否需在转向后将下拉列表框的选项恢复至默认的提示项。 函数的实现通过获取`selObj`中当前选定的`selectedIndex`对应的`value`属性值,并将其赋予`...
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值