P1235 血缘关系

题目描述

我们正在研究妖怪家族的血缘关系。每个妖怪都有相同数量的基因,但是不同的妖怪的基因可能是不同的。我们希望知道任意给定的两个妖怪之间究竟有多少相同的基因。由于基因数量相当庞大,直接检测是行不通的。但是,我们知道妖怪家族的家谱,所以我们可以根据家谱来估算两个妖怪之间相同基因的数量。

妖怪之间的基因继承关系相当简单:如果妖怪C是妖怪A和B的孩子,则C的任意一个基因只能是继承A或B的基因,继承A或B的概率各占50%。所有基因可认为是相互独立的,每个基因的继承关系不受别的基因影响。

现在,我们来定义两个妖怪X和Y的基因相似程度。例如,有一个家族,这个家族中有两个毫无关系(没有相同基因)的妖怪A和B,及它们的孩子C和D。那么C和D相似程度是多少呢?因为C和D的基因都来自A和B,从概率来说,各占50%。所以,依概率计算C和D平均有50%的相同基因,C和D的基因相似程度为50%。需要注意的是,如果A和B之间存在相同基因的话,C和D的基因相似程度就不再是50%了。

你的任务是写一个程序,对于给定的家谱以及成对出现的妖怪,计算它们之间的基因相似程度。

输入格式

第一行两个整数n和k。n(2≤n≤300)表示家族中成员数,它们分别用1, 2, …, n来表示。k(0≤k≤n-2)表示这个家族中有父母的妖怪数量(其他的妖怪没有父母,它们之间可以认为毫无关系,即没有任何相同基因)。

接下来的k行,每行三个整数a, b, c,表示妖怪a是妖怪b的孩子。

然后是一行一个整数m(1≤m≤n2),表示需要计算基因相似程度的妖怪对数。

接下来的m行,每行两个整数,表示需要计算基因相似程度的两个妖怪。

你可以认为这里给出的家谱总是合法的。具体来说就是,没有任何的妖怪会成为自己的祖先,并且你也不必担心会存在性别错乱问题。

输出格式

共m行。可k行表示第k对妖怪之间的基因相似程度。你必须按百分比输出,有多少精度就输出多少,而且必须准确,但不允许出现多余的0(注意,0.001的情况应输出0.1%,而不是.1%)。具体格式参见样例。

输入输出样例

输入 #1复制

7 4                                                   
4 1 2                                         
5 2 3                                         
6 4 5                                         
7 5 6
4
1 2
2 6
7 5
3 3

输出 #1复制

0%
50%
81.25%
100%

代码来咯:

/*luoguP1235 AC code, Copyright © JSSY(i.e. jiangyougogogo)*/
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<ctime>
#include<cctype>
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
#define hk 310
using namespace std;
struct DB{short N[hk];};//正序存储定点小数,N[0]代表位数,N[1]是整数部分,N[2]到N[N[0]]是小数部分
DB div(DB x,int y){//高精度除法
    int rem,flg=0;DB z;if(!x.N[1]&&x.N[0]==1)return x;
    for(memset(z.N,0,sizeof z.N),z.N[z.N[0]=1]=x.N[1]/y,rem=x.N[1]%y;(rem||!flg)||z.N[0]<x.N[0];z.N[0]++){
        z.N[z.N[0]+1]=(rem*10+x.N[z.N[0]+1])/y;
        rem=(rem*10+x.N[z.N[0]+1])%y;if(z.N[z.N[0]+1])flg=1;
    }return z;
}DB plu(DB x,DB y){//高精度加法
    int i=max(x.N[0],y.N[0]);DB z;for(memset(z.N,0,sizeof z.N);i;i--){
        z.N[i-1]+=(z.N[i]+x.N[i]+y.N[i])/10;
        z.N[i]=(z.N[i]+x.N[i]+y.N[i])%10;
    }for(z.N[0]=max(x.N[0],y.N[0]);z.N[0]>1&&!z.N[z.N[0]];z.N[0]--);
    return z;
}void wri(DB x){//输出,先输出百分数的整数部分,再输出小数部分
    int i,t=0;for(i=1;i<4;i++)t=t*10+x.N[i];
    printf("%d",t);if(x.N[0]<4){puts("%");return;}
    for(putchar('.'),i=4;i<=x.N[0];i++)printf("%d",x.N[i]);puts("%");return;
}
vector<int>eg[hk];int n,k,i,j,x,y,z,f[hk][2],m,q[hk],la[hk],to[hk],hd,tl,tt;
bool iss[hk],lab[hk][hk],inq[hk];DB F[hk][hk];
DB C(int x,int y){//记忆化搜索
    if(lab[x][y])return F[x][y];//计算过就直接把答案拿来
    if(la[x]>la[y]||!iss[y])F[x][y]=F[y][x]=div(plu(C(f[x][0],y),C(f[x][1],y)),2);
    else F[x][y]=F[y][x]=div(plu(C(x,f[y][0]),C(x,f[y][1])),2);//选辈分低的向祖先方向继续搜
    lab[x][y]=lab[y][x]=1;return F[x][y];
}int main(){
for(scanf("%d%d",&n,&k),i=0;i<k;i++)
scanf("%d%d%d",&x,&y,&z),f[x][0]=y,f[x][1]=z,iss[x]=1,eg[y].push_back(x),eg[z].push_back(x),to[x]+=2;
    for(i=1;i<=n;i++)if(!iss[i])q[++tl]=i,la[i]=inq[i]=1;//类似拓扑的方法处理辈分,先把没有父母(即iss[i]==0)的那些点记作第一层,计算la[]表示层数,分层
    for(hd=0;hd^tl;){
        for(hd++,i=eg[q[hd]].size()-1;i+1;i--)if(!(--to[tt=eg[q[hd]][i]])&&!inq[tt])
            q[++tl]=tt,inq[tt]=1,la[tt]=la[q[hd]]+1;
}//BFS拓扑
for(i=1;i<=n;i++)for(j=1;j<=n;j++){
        if(!iss[i]&&!iss[j])F[i][j].N[1]=0,F[i][j].N[0]=1,lab[i][j]=1;
        if(i==j)F[i][j].N[1]=1,F[i][j].N[0]=1,lab[i][j]=1;
}//赋初值,其中lab[i][j]表示当前位置有没有被计算过
for(scanf("%d",&m);m;m--){
        scanf("%d%d",&x,&y);
        wri(C(x,y));//计算即输出
    }return 0;
}

求点赞加关注!!!! 

 

Hive 是一个基于Hadoop 架构的数据仓库工具,它提供了一个SQL 接口,用于查询和分析存储在Hadoop 分布式文件系统(HDFS) 上的大数据集。Hive 通过元数据管理来跟踪数据的血缘关系,这是通过解析Hive 查询计划来实现的。 当用户提交一个Hive 查询时,Hive 会将其转化为一个查询计划,该计划由一系列的MapReduce 任务或Spark 作业组成。在查询计划中,Hive 会记录每个任务或作业所需的输入数据和输出数据的信息。 为了解析血缘关系,Hive 在元数据中维护了每个表的相关信息,包括表的名称、列的字段和分区信息等。在查询计划中,Hive 会根据需要访问的表和列的信息来构建血缘关系。通过追踪查询计划中的输入输出操作,Hive 可以确定每个数据操作的来源。 具体而言,当Hive 解析一个查询计划时,它会记录每个任务或作业所操作的表和列。对于输入数据,Hive 可以确定该数据是从哪个表、哪个列中选取的。对于输出数据,Hive 可以确定该数据将被写入到哪个表、哪个列中。 通过解析血缘关系,Hive 可以提供一些有用的功能。首先,它可以帮助用户了解数据的来源,确保数据的准确性。其次,它可以帮助用户跟踪数据流转的路径,方便故障排查和调试。最后,它还可以支持数据血缘的追踪和分析,帮助用户了解数据的派生过程和相关性。 总而言之,Hive 通过解析查询计划来构建和维护数据的血缘关系。这种血缘关系的解析可以帮助用户了解数据的来源、跟踪数据流转、支持数据血缘的追踪和分析等功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值