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;
}
引用\[1\]中提到了一种形动态规划的方法来解决CodeForces - 982C问题。在这个问题中,subtree指的是子连通块,而不是子。为了使cnt_white - cnt_black尽可能大,可以使用两次形动态规划来求解。第一次是自底向上的过程,维护一个dp数组,表示以每个节点为根的子中的最大连通块。第二次是自顶向下的过程,处理自底向上过程中无法包含的链所代表的子。在第二次遍历中,需要维护一个sum变量,用于存储链所代表的子的贡献。根据ans\[u\]的正负,决定是否能对相邻的子节点做出贡献。如果ans\[u\]为正,则减去dp\[v\]就是链所代表的子的权值。最终,ans\[u\]代表包含节点u在内的子连通块的最大权值。\[1\] 问题: CodeForces - 982C 形DP是什么问题?如何解决? 回答: CodeForces - 982C是一个形动态规划问题。在这个问题中,需要求解子连通块的最大权值和,使得cnt_white - cnt_black尽可能大。解决这个问题的方法是使用两次形动态规划。第一次是自底向上的过程,维护一个dp数组,表示以每个节点为根的子中的最大连通块。第二次是自顶向下的过程,处理自底向上过程中无法包含的链所代表的子。在第二次遍历中,需要维护一个sum变量,用于存储链所代表的子的贡献。根据ans\[u\]的正负,决定是否能对相邻的子节点做出贡献。最终,ans\[u\]代表包含节点u在内的子连通块的最大权值。\[1\] #### 引用[.reference_title] - *1* *2* [CodeForces - 1324F Maximum White Subtree(形dp)](https://blog.youkuaiyun.com/qq_45458915/article/details/104831678)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值