题解 猴猴吃苹果 长链剖分

题解 猴猴吃苹果

题目描述

屏幕截图_5.png

具体做法与心路历程

比较简单吧。题目要求我们每次找最长的链走,然后删去点权。

k k k为根,我们发现如下性质:

  • 走的路径一定是叶子节点
  • 每个点走后就没有贡献了

我们把一颗树画出来,观察即可发现,这就是长链剖分!!!

我们把链的长度赋给叶子节点,然后排序即可。

注意排序的比较!!!

C o d e \mathcal{Code} Code

/*******************************
Author:galaxy yr
LANG:C++
Created Time:2019年10月30日 星期三 14时40分57秒
*******************************/
#include<cstdio>
#include<algorithm>

using namespace std;

struct IO{
    template<typename T>
    IO & operator>>(T&res)
    {
        T q=1;char ch;
        while((ch=getchar())<'0' or ch>'9')if(ch=='-')q=-q;
        res=(ch^48);
        while((ch=getchar())>='0' and ch<='9') res=(res<<1)+(res<<3)+(ch^48);
        res*=q;
        return *this;
    }
}cin;

struct edge{
    int to,next;
    edge(int a=0,int b=0):to(a),next(b){}
};

const int maxn=5e4+10;
int n,rt,len[maxn],son[maxn],p[maxn],head[maxn],cnt,dis[maxn],stk[maxn],_top;
edge e[maxn<<1];

void add(int u,int v)
{
    e[++cnt]=edge(v,head[u]);
    head[u]=cnt;
}

void dfs(int now,int fa)
{
    len[now]=1;
    for(int i=head[now];i;i=e[i].next)
        if(e[i].to!=fa)
        {
            dfs(e[i].to,now);
            if(len[son[now]]<len[e[i].to] || (len[son[now]]==len[e[i].to] && p[e[i].to]<p[son[now]]))
                son[now]=e[i].to;
        }
    if(!son[now])
    {
        stk[++_top]=now;
        p[now]=now;
    }
    else
        p[now]=p[son[now]];
    len[now]=len[son[now]]+1;
}

void dfs(int now,int res,int fa)
{
    if(!son[now])
    {
        dis[now]=res;
        return;
    }
    else
        dfs(son[now],res+1,now);
    for(int i=head[now];i;i=e[i].next)
        if(e[i].to!=fa && e[i].to!=son[now])
            dfs(e[i].to,1,now);
}

bool cmp(int a,int b)
{
    if(dis[a]==dis[b]) return a<b;
    return dis[a]>dis[b];
}

int main()
{
    //freopen("apple.in","r",stdin);
    //freopen("apple.out","w",stdout);
    cin>>n>>rt;
    int u;
    rt++;
    for(int i=2;i<=n;i++)
    {
        cin>>u; u++;
        add(u,i);
        add(i,u);
    }
    dfs(rt,0);
    dfs(rt,1,0);
    sort(stk+1,stk+_top+1,cmp);
    printf("%d\n",rt-1);
    for(int i=1;i<=_top;i++)
        printf("%d\n",stk[i]-1);
    return 0;
}
//14:55
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值