1 条题解

  • 0
    @ 2025-8-24 22:14:04

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar hepp
    如此成绩||不拿2级勾不删||调题相关:https://www.luogu.me/article/k3zgtcrs

    搬运于2025-08-24 22:14:04,当前版本为作者最后更新于2022-04-17 20:15:29,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    这两个小题的解法不相同!

    我们先看第一小题。

    每个人都最多只有一个已知的比他成绩好的,我们可以把比他成绩好的人看做他的父节点,这样,我们就得到了一棵树。而这棵树的根节点是 0。

    看样例,要使编号小的排在前面,我们一开始先考虑 1(不惜一切代价将未放入的编号最小的放入), 1 的祖先有 3,2 。要放入 1,就必须先放入 2,3 (他的所有祖先)。所以,现在的排名是:

    NO.1 2 号

    NO.2 3 号

    NO.3 1 号

    1,2,3 号都已排名,还剩一个 4,4 的祖先 2 已经在队伍中,所以他可以直接进入,最终排名 2 3 1 4。按照题目的排列方式(第 ii 个数输出 ii 的名次)答案是 3 1 2 4 。

    代码如下:

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1e5*2+10;
    int ans[N],a[N],cur=0;
    //ans[i]指第i名的名次,a[i]是第i人的祖先,cur代表队伍中的人数。
    int pm(int x)
    {
        if(ans[x]!=-1)//x以及x的祖先已经入队了
            return cur;
        else
            return ans[x]=pm(a[x])+1;//递推,x是他的祖先的下一个
    }
    int main()
    {
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]);
        for(int i=0;i<=n;i++)
            ans[i]=-1;
        ans[0]=0;
        //初始化
        for(int i=1;i<=n;i++)
        {
            if(ans[i]==-1)//如果他还没有进队
            {
               ans[i]=pm(i);//让他进队
               cur=ans[i];//现在有ans[i]个人在队伍中
            }
        }
        for(int i=1;i<=n;i++)
            printf("%d ",ans[i]);
        printf("\n");
        return 0;
    }
    

    第二小题

    如题目所述,让编号前的同学靠后不等于编号后的同学靠前。(例如 4 0 0 0 1 应该得出的是 3 2 4 1 而不是 1 4 3 2 ) 我们在第一题时是从多点到根点,而这一题我们需要从根点到多点,类似于 bfs 和拓扑。

    还是样例。与 0 联通的只有 2,所以我们只能请 2 入队。 2 入队后,他的儿子便可以入队了。他的儿子有 3,4 两位,但我们要让编号小的后入队,所以 4 先入队,接着是 3。3 的儿子——1 也解禁并入队,最终排名 2 4 3 1,答案是 4 1 3 2。

    转化为代码如下:

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1e5*2+10;
    priority_queue<int> q;//优先队列,编号大的在前
    int cnt[N],a[N],cur=0;
    vector<int>g[N];
    int main()
    {
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            g[a[i]].push_back(i);
        }
        //标注出每个人的所有儿子
        for(int i=0;i<=n;i++)
            cnt[i]=-1;
        //初始化
        q.push(0);
        //与0点联通的点(没有父节点)可以进队
        while(q.size())
        {
            int u=q.top();
            q.pop();
            cnt[u]=cur;
            cur++;
            //在队伍中编号最大的出队并获取排名
            for(int i=0;i<g[u].size();i++)
                q.push(g[u][i]);
            //u的儿子也可以获取排名了
        }
        for(int i=1;i<=n;i++)
            printf("%d ",cnt[i]);
        return 0;
    }
    
    • 1

    信息

    ID
    5079
    时间
    1000ms
    内存
    125MiB
    难度
    3
    标签
    递交数
    0
    已通过
    0
    上传者