bzoj3631 [JLOI2014]松鼠的新家 树链剖分

Description


松鼠的新家是一棵树,前几天刚刚装修了新家,新家有n个房间,并且有n-1根树枝连接,每个房间都可以相互到达,且俩个房间之间的路线都是唯一的。天哪,他居然真的住在“树”上。松鼠想邀请小熊维尼前来参观,并且还指定一份参观指南,他希望维尼能够按照他的指南顺序,先去a1,再去a2,……,最后到an,去参观新家。
可是这样会导致维尼重复走很多房间,懒惰的维尼不听地推辞。可是松鼠告诉他,每走到一个房间,他就可以从房间拿一块糖果吃。维尼是个馋家伙,立马就答应了。
现在松鼠希望知道为了保证维尼有糖果吃,他需要在每一个房间各放至少多少个糖果。因为松鼠参观指南上的最后一个房间an是餐厅,餐厅里他准备了丰盛的大餐,所以当维尼在参观的最后到达餐厅时就不需要再拿糖果吃了。

2<= n <=300000

Solution


很裸的树链剖分,注意一段路程的终点和上一段路程的起点会重复,要删掉一个

新年前的最后一题了吧。我比较弱,不敢打cf也做不出什么算马,但是我觉的coding真的很有趣很好玩。虽然有时候也会很累,但是看到自己的努力会有成果还是很开心的。通过竞赛认识了很多有趣的人,也希望可以一直走下去看更高处的风景
//hello2018

Code


//Hello 2018
#include <stdio.h>
#include <string.h>
#include <algorithm>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define drp(i,st,ed) for (int i=st;i>=ed;--i)
#define fill(x,t) memset(x,t,sizeof(x))
using std:: swap;
const int INF=0x3f3f3f3f;
const int N=300005;
const int E=700005;
struct edge{int x,y,next;}e[E];
int ls[N],edCnt=0;
int lazy[N<<2],sum[N<<2],a[N];
int size[N],dep[N],pos[N],bl[N],fa[N];
int n;
int read() {
    int x=0,v=1; char ch=getchar();
    for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
    for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
    return x*v;
}
void addEdge(int x,int y) {
    e[++edCnt]=(edge){x,y,ls[x]}; ls[x]=edCnt;
    e[++edCnt]=(edge){y,x,ls[y]}; ls[y]=edCnt;
}
void dfs1(int now,int father) {
    size[now]=1;
    for (int i=ls[now];i;i=e[i].next) {
        if (e[i].y==father) continue;
        dep[e[i].y]=dep[now]+1;
        fa[e[i].y]=now;
        dfs1(e[i].y,now);
        size[now]+=size[e[i].y];
    }
}
void dfs2(int now,int up) {
    pos[now]=++pos[0];
    bl[now]=up;
    int mx=0;
    for (int i=ls[now];i;i=e[i].next) {
        if (dep[e[i].y]<=dep[now]||size[e[i].y]<=size[mx]) continue;
        mx=e[i].y;
    }
    if (!mx) return ;
    dfs2(mx,up);
    for (int i=ls[now];i;i=e[i].next) {
        if (dep[e[i].y]<=dep[now]||e[i].y==mx) continue;
        dfs2(e[i].y,e[i].y);
    }
}
void push_up(int now) {
    sum[now]=sum[now<<1]+sum[now<<1|1];
}
void push_down(int now,int tl,int tr) {
    if (!lazy[now]) return ;
    int mid=(tl+tr)>>1;
    sum[now<<1]+=(mid-tl+1)*lazy[now];
    sum[now<<1|1]+=(tr-mid)*lazy[now];
    lazy[now<<1]+=lazy[now];
    lazy[now<<1|1]+=lazy[now];
    lazy[now]=0;
}
int query(int now,int tl,int tr,int l,int r) {
    if (tl==l&&tr==r) return sum[now];
    push_down(now,tl,tr);
    int mid=(tl+tr)>>1;
    if (r<=mid) return query(now<<1,tl,mid,l,r);
    else if (l>mid) return query(now<<1|1,mid+1,tr,l,r);
    else return query(now<<1,tl,mid,l,mid)+query(now<<1|1,mid+1,tr,mid+1,r);
}
void modify(int now,int tl,int tr,int l,int r,int v) {
    if (tl==l&&tr==r) {
        sum[now]+=(tr-tl+1)*v;
        lazy[now]+=v;
        return ;
    }
    push_down(now,tl,tr);
    int mid=(tl+tr)>>1;
    if (r<=mid) modify(now<<1,tl,mid,l,r,v);
    else if (l>mid) modify(now<<1|1,mid+1,tr,l,r,v);
    else {
        modify(now<<1,tl,mid,l,mid,v);
        modify(now<<1|1,mid+1,tr,mid+1,r,v);
    }
    push_up(now);
}
void buildTree(int now,int tl,int tr) {
    sum[now]=0;
    if (tl==tr) return ;
    int mid=(tl+tr)>>1;
    buildTree(now<<1,tl,mid);
    buildTree(now<<1|1,mid+1,tr);
}
void add_candy(int x,int y) {
    while (bl[x]!=bl[y]) {
        if (dep[bl[x]]<dep[bl[y]]) swap(x,y);
        modify(1,1,n,pos[bl[x]],pos[x],1);
        x=fa[bl[x]];
    }
    if (pos[x]>pos[y]) swap(x,y);
    modify(1,1,n,pos[x],pos[y],1);
}
int main(void) {
    n=read();
    rep(i,1,n) a[i]=read();
    rep(i,2,n) {
        int x=read(),y=read();
        addEdge(x,y);
    }
    buildTree(1,1,n);
    dfs1(1,0);
    dfs2(1,1);
    rep(i,2,n) {
        add_candy(a[i-1],a[i]);
        modify(1,1,n,pos[a[i]],pos[a[i]],-1);
    }
    rep(i,1,n) printf("%d\n", query(1,1,n,pos[i],pos[i]));
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值