[JLOI2014]松鼠的新家

洛谷3258

题目描述

松鼠的新家是一棵树,前几天刚刚装修了新家,新家有n个房间,并且有n-1根树枝连接,每个房间都可以相互到达,且俩个房间之间的路线都是唯一的。天哪,他居然真的住在”树“上。

松鼠想邀请小熊维尼前来参观,并且还指定一份参观指南,他希望维尼能够按照他的指南顺序,先去a1,再去a2,......,最后到an,去参观新家。可是这样会导致维尼重复走很多房间,懒惰的维尼不停地推辞。可是松鼠告诉他,每走到一个房间,他就可以从房间拿一块糖果吃。

维尼是个馋家伙,立马就答应了。现在松鼠希望知道为了保证维尼有糖果吃,他需要在每一个房间各放至少多少个糖果。

因为松鼠参观指南上的最后一个房间an是餐厅,餐厅里他准备了丰盛的大餐,所以当维尼在参观的最后到达餐厅时就不需要再拿糖果吃了。

输入输出格式

输入格式:

第一行一个整数n,表示房间个数第二行n个整数,依次描述a1-an接下来n-1行,每行两个整数x,y,表示标号x和y的两个房间之间有树枝相连。

输出格式:

一共n行,第i行输出标号为i的房间至少需要放多少个糖果,才能让维尼有糖果吃。

输入输出样例

输入样例#1: 复制
5
1 4 5 3 2
1 2
2 4
2 3
4 5
输出样例#1: 复制
1
2
1
2
1

说明

2<= n <=300000

分析:首先很容易看出是树链剖分

本题先告诉你是一棵树,然后要这只小熊在树上走,每经过一个点都要放一颗糖果,问每个点放了几个糖果。这显然是对树上的链进行操作,这就想到了树链剖分。

其实就是一个模板。

但本题要注意的是每次到达一个点,要放一次糖果,但你从这个点走向下一个点的时候就不要放糖果了,所以每次再在这个点上进行减一就行了。

传送门:洛谷3258

 

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define ll(x) (x*2)
#define rr(x) (x*2+1)
typedef long long ll;
int lazy[600100],last[600100],w[600100],dep[600010],fa[600010],n,len=0;
int siz[600010],son[600100],cnt=0,id[600100],wt[600100],sum[2000100];
int top[600100],kkk[600100],yuan[600010],ansk[600010];
struct node
{
    int to,next;
}a[600100];
void add(int a1,int a2)
{
    len++;
    a[len].to=a2;
    a[len].next=last[a1];
    last[a1]=len;
}
void dfs1(int x,int father,int deep)
{
    dep[x]=deep;
    fa[x]=father;
    siz[x]=1;
    int maxson=-1;
    for(int i=last[x];i;i=a[i].next)
    {
        int to=a[i].to;
        if(to==father)
        continue;
        dfs1(to,x,deep+1);
        siz[x]+=siz[to];
        if(siz[to]>maxson) son[x]=to,maxson=siz[to];
    }
}
void dfs2(int x,int topf)
{
    cnt++;
    id[x]=cnt;
    yuan[cnt]=x;
    wt[cnt]=w[x];
    top[x]=topf;
    if(!son[x]) return;
    dfs2(son[x],topf);
    for(int i=last[x];i;i=a[i].next)
    {
        int to=a[i].to;
        if(to==fa[x]||to==son[x]) continue;
        dfs2(to,to);
    }
}
void xia(int node,int lsum,int rsum)
{
    if(lazy[node])
    {
        lazy[ll(node)]+=lazy[node];
        lazy[rr(node)]+=lazy[node];
        sum[ll(node)]+=lsum*lazy[node];
        sum[rr(node)]+=rsum*lazy[node];
        lazy[node]=0;
        return;
    }
}
void gai(int node,int left,int right,int l,int r,int k)
{
    if(l<=left&&right<=r)
    {
        sum[node]+=(k*(right-left+1));
        lazy[node]+=k;
        return;
    }
    if(left>r||right<l) return;
    int mid=(left+right)/2;
    xia(node,mid-left+1,right-mid);
    if(left<=mid) gai(ll(node),left,mid,l,r,k);
    if(right>mid) gai(rr(node),mid+1,right,l,r,k);
    sum[node]=(sum[ll(node)]+sum[rr(node)]);
}
void gai1(int x,int y,int k)
{
    int yy=y;
    while(top[x]!=top[y])
    {
        if(dep[top[x]]<dep[top[y]]) swap(x,y);
        gai(1,1,n,id[top[x]],id[x],k);
        x=fa[top[x]];
    //    cout<<4554545;
    }
    if(dep[x]>dep[y]) swap(x,y);
    gai(1,1,n,id[x],id[y],k);
    gai(1,1,n,id[yy],id[yy],-k);
}
void de(int node,int left,int right)
{
    if(left==right) 
    {
        ansk[yuan[left]]=sum[node];
        //cout<<node<<endl;
        return;
    }
    int mid=(left+right)/2;
    xia(node,mid-left+1,right-mid);
    de(ll(node),left,mid);
    de(rr(node),mid+1,right);
}
int main()
{
    int m,r,x,y;
    cin>>n;
    for(int i=1;i<=n;i++)
    w[i]=0;
    for(int i=1;i<=n;i++)
    scanf("%d",&kkk[i]);
    for(int i=1;i<=n-1;i++)
    {
        scanf("%d%d",&x,&y);
        add(x,y);
        add(y,x);
    }
    dfs1(1,0,1);
    dfs2(1,1);
    for(int i=1;i<n;i++)
    {    
        gai1(kkk[i],kkk[i+1],1);
    }
    de(1,1,n);
    for(int i=1;i<=n;i++)
    printf("%d\n",ansk[i]);
}

 

 

 

转载于:https://www.cnblogs.com/kzj-pwq/p/8179689.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值