并查集+模拟构造 sgu 449. Dendrograms

题目链接:

http://acm.sgu.ru/problem.php?contest=0&problem=449

题目意思:

这题目真难读懂。

有n个节点,m条水平线,告诉每条水平线下竖线的数量以及每个竖线下的节点值。问怎样组合该树,使得该树结构清晰(竖线和横线不交叉)且靠左边的节点尽可能小。

解题思路:

先按水平线的高度从大到小排序,高度大的在下面,也就是从下往上处理,对每条水平线,把他所有的竖线连成一个连通块,并新建一个共同的父亲节点(两个属性,一个id,一个孩子节点的最小值),最后把所有的树联合在一起,凑成一颗完整的树结构。也就是虚拟一条水平线,它包含所有的节点。建好图后,对每个节点对孩子节点按值从小到大排序,最后dfs把叶子输出来就行了。

代码:

//#include<CSpreadSheet.h>

#include<iostream>
#include<cmath>
#include<cstdio>
#include<sstream>
#include<cstdlib>
#include<string>
#include<string.h>
#include<cstring>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<stack>
#include<list>
#include<queue>
#include<ctime>
#include<bitset>
#define eps 1e-6
#define INF 0x3f3f3f3f
#define PI acos(-1.0)
#define ll __int64
#define LL long long
#define lson l,m,(rt<<1)
#define rson m+1,r,(rt<<1)|1
#define M 1000000007
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;

#define Maxn 110000

struct Node
{
    int id,va;

    Node(int a=0,int b=0)
    {
        id=a;
        va=b;
    }
    friend bool operator < (struct Node a,struct Node b) //按节点大小从小到大排序
    {
        return a.va<b.va;
    }

}node[Maxn<<2]; //类似于线段树 合并点 并查集处理

vector<Node>myv[Maxn*4]; //存每棵树的子树节点情况
vector<int>pp[Maxn]; //子树包含关系
int n,m,q;
int ans[Maxn];
map<int,int>myp; //map中是按第一关键字从小到大排序的
int a[Maxn]; //按高度从大到小进行处理
int b[Maxn];//缓存当前子树节点的情况
int fa[Maxn<<2],cnt;

int find(int x)
{
   if(fa[x]!=x)
        fa[x]=find(fa[x]); //边查找变压缩
   return fa[x];
}

void init()
{
    myp.clear();

    for(int i=1;i<=n;i++)
        pp[i].clear();

    for(int i=1;i<=4*n;i++) //初始化fa数组
    {
         fa[i]=i;
         myv[i].clear();
    }

    int tt=0;  //给给定的子树标号

    for(int i=1;i<=m;i++)
    {
        ++tt;
        int cur,nn;

        scanf("%d%d",&cur,&nn);
        myp[cur]=tt; //记录标号
        while(nn--)
        {
            int temp;
            scanf("%d",&temp);
            pp[tt].push_back(temp); //该标号下的节点
        }
    }
    ++tt; //为了最终凑成一颗树,增加一个虚拟的组合情况
    myp[0]=tt;//放到最后 把所有的树整合在一起 凑成一棵树
    for(int i=1;i<=n;i++)
        pp[tt].push_back(i); //该虚拟树包含所有节点
    m++;
    for(map<int,int>::iterator it=myp.begin();it!=myp.end();it++)
        a[tt--]=it->second; //相当于从大到小排序,记录标号

}
void dfs(int cur) //图建好了后 只用跑一边dfs就可以把叶子节点按从左到右的顺序找到就行了
{
    if(!myv[cur].size()) //到达了叶子节点
    {
        ans[++cnt]=node[cur].va; //把该棵树的值记录
        return ;
    }
    for(int i=0;i<myv[cur].size();i++) //依次往下扫
        dfs(myv[cur][i].id);
}

void Cal()
{
    for(int i=1;i<=n;i++)
    {
        node[i].id=i;
        node[i].va=i; //最初只有n颗只含一个叶子节点的树
    }
    for(int i=1;i<=m;i++)
    {
        int cur=a[i]; //处理当前的

        for(int j=0;j<pp[cur].size();j++)
            b[j]=find(pp[cur][j]); //找到各子节点所在的树根
        sort(b,b+pp[cur].size()); //从小到大排序
        int kk=unique(b,b+pp[cur].size())-b; //去掉重复的子树
        ++n; //新建一个节点 归并所有的子树
        node[n].id=n;
        node[n].va=n;

        for(int j=0;j<kk;j++) //建图
        {
            int temp=b[j];
            fa[temp]=n; //所有的子树的父亲归并

            node[n].va=min(node[n].va,node[temp].va); //用值最小的叶子节点代替
            myv[n].push_back(Node(temp,node[temp].va)); //建树
        }

    }
    for(int i=1;i<=n;i++) //对每颗子树 根据值排序
        sort(myv[i].begin(),myv[i].end()); //子树的键值小的排在前面 然后一边dfs就行了
    cnt=0;
    dfs(n); //从最后一个总节点出发
    for(int i=1;i<=q;i++)
    {
        int cur;
        scanf("%d",&cur);
        printf("%d\n",ans[cur]);
    }

}

int main()
{
   //freopen("in.txt","r",stdin);
   //freopen("out.txt","w",stdout);
   while(~scanf("%d%d%d",&n,&m,&q))
   {
       init();
       Cal();
   }

   return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值