CF——1611D,图论构造题打卡

愿题目翻译:

 

 

您将得到一个由𝑛顶点组成的有根树。顶点编号从1到𝑛。任何顶点都可以是树的根。 

树是没有循环的连通无向图。有根树是一棵有选定顶点的树,称为根。

树由包含𝑛数字的祖先数组𝑏指定:𝑏𝑖是数字为𝑖的顶点的祖先。顶点𝑢的祖先是一个顶点,它是从𝑢到根的简单路径上的下一个顶点。例如,在从5到3(根)的简单路径上,下一个顶点是1,因此5的祖先是1。

根没有祖先,因此对于它,𝑏𝑖的值是𝑖(根是𝑏𝑖=𝑖的唯一顶点)。

例如,如果𝑛=5和𝑏=[3,1,3,3,1],那么树看起来像这样。

一个𝑛=5的根树示例,树的根是顶点数3。
给定一个数组𝑝—树的顶点排列。如果可能的话,在边缘上赋任何正整数的权重,这样顶点按到根的距离排序就会形成给定的排列𝑝。

换句话说,对于给定的顶点排列𝑝,有必要选择这样的边权值,使条件𝑑𝑖𝑠𝑡[𝑝𝑖]<𝑑𝑖𝑠𝑡[𝑝𝑖+1]对于从1到𝑛−1的每个𝑖都成立。𝑑𝑖𝑠𝑡[𝑢]是从根到𝑢的路径上的边的权值之和。特别是,如果顶点𝑢是树的根,则𝑑𝑖𝑠𝑡[𝑢]=0。

例如,假设𝑝=[3,1,2,5,4]。在这种情况下,下面的边权满足这个排列:

边(3,4)的权值为102;
边(3,1)的权值为1;
边(1,2)的权值为10;
边(1,5)的权值是100。
到根的距离数组看起来像:𝑑𝑖𝑠𝑡=[1,11,0102,101]。通过增加到根的距离来排序的顶点形成给定的排列𝑝。

打印所需的边权值,或者确定没有合适的方法分配权值。如果有几个解决方案,那么打印其中任何一个。

输入
第一行输入数据包含一个整数𝑡(1≤𝑡≤104)—测试中输入数据集的数量。

每个测试用例由三行组成。

其一为整数𝑛(1≤𝑛≤2⋅105)。它是树中顶点的数量。

第二行包含𝑛整数𝑏1𝑏2,…,𝑏𝑛(1≤𝑏𝑖≤𝑛)。可以保证𝑏数组编码了一些根树。

第三行包含不同整数𝑝1,𝑝2,…,𝑝𝑛(1≤𝑝𝑖≤𝑛)的给定排列𝑝:𝑛。

保证测试中所有测试用例𝑛的值之和不超过2⋅105。

输出
对于每一组输入数据,将答案打印在单独的一行上。

如果解决方案存在,则打印𝑛整数数组𝑤1,𝑤2,…,𝑤𝑛,其中𝑤𝑖是从𝑏𝑖到𝑖的边的权值。对于根结点没有这样的边,因此使用值𝑤𝑖=0。对于所有其他顶点,𝑤𝑖的值必须满足不等式1≤𝑤𝑖≤109。𝑤𝑖值之间可以有相等的数,但是从根到顶点的所有边的权重之和必须不同,并且满足给定的排列。

如果有几个解,输出其中任何一个。

如果不存在,则输出-1。

例子
inputCopy
4
5
3 1 3 3 3 1
3 1 2 5 4
3.
1 1 2
3 1 2
7
1 1 2 3 4 5 6
1 2 3 4 5 6 7
6
4 4 4 4 4 1 1
4 2 1 5 6 3
outputCopy
1 100 102 100
-1
0 3 100 1 1 2 4
6 5 10 0 2 3
请注意
在该语句的主要部分分析了该示例的第一组输入数据。

在该示例的第二组输入数据中,不可能分配正权重来获得给定的顶点排列。

———————————————————————————————————————————

大致题意:给你一棵树,按照所给的根节点排列要求分配边权的值。如果可以满足该排列,则输出,每个边权所赋的值,不存在则输出-1。

———————————————————————————————————————————

刚开始以为要建图遍历等等,后来发现,满足该排列的条件。

到达根节点的dist值一定比父亲节的dist的值要大。

换而言之,如果要符合,那么在P[i]出现之前,那么它的父节点之前已经出现过了。

所以我们可以直接给边暴力赋值,每次都让它根据上一条边递增,如果上一条边不存在,则表示在P[i]的后面,不符合,直接return -1即可

——————————————————————————————————————————— 下面是代码的实现:

#include <iostream>
#include <algorithm>
#include <string.h>
#include <string>
#include <math.h>
#include <stdio.h>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<tuple>
#include<numeric>
#include<stack>
using namespace::std;
typedef long long  ll;
int n,t,m;
int as[200005];
int a[200005];
void solv(){
    cin>>n;
    vector<int>q[n+1];
    int root;
    for (int i =1; i<=n; i++) {
        cin>>as[i];
        if (as[i]==i) {
            root=i;
        }
    }
    for (int i =1; i<=n; i++) {
        cin>>a[i];
    }
    if (a[1]!=root) {
        printf("-1\n");
        return;
    }
    int dis[200005];
    for (int i =0; i<=n; i++) {
        dis[i]=-1;
    }
    dis[root]=0;
    for (int i =2; i<=n; i++) {
        if (dis[as[a[i]]]==-1) {
            printf("-1\n");return;
        }
        dis[a[i]]=dis[a[i-1]]+1;
    }
    for (int i =1; i<=n; i++) {
        printf("%d ",dis[i]-dis[as[i]]);
    }
    printf("\n");
    
    
    
    
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(); cout.tie();
    cin>>t;
    while (t--) {
        solv();
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值