树链剖分——BZOJ3631/Luogu3258 [JLOI2014]松鼠的新家

题面:BZOJ3631 Luogu3258
简要题面:每次修改树上一条链的值(+1),最后询问每个点的值
裸的树剖,内套区间修改线段树
我本来还以为这题码量可以稍微少一点(因为一开始线段树上都为0),可惜的是我其他还记录着东西
所以代码还是光荣地快上百行了
最后输出时候要注意一点,因为每个中转点(就是链的端点)都被多算了一次(第一个和最后一个除外),因为中转点各属于两条不同的链
再加上最后一个点最后到的时候没有糖果,所以最后除了第一个点其他所有点在输出时-1

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<iostream>
#include<cstdlib>
#include<string>
#include<ctime>
#include<queue>
#include<climits>
using namespace std;
int a[300001];
int n,nedge=0,p[600001],nex[600001],head[600001];
int fa[300001],deep[300001],s[300001],son[300001],top[300001];
int sx[300001],xs[300001],ne=0;
int lt[1200001],rt[1200001],t[1200001],add[1200001];
inline void addedge(int a,int b){p[++nedge]=b;nex[nedge]=head[a];head[a]=nedge;}
inline void dfs(int x,int Fa,int dep){
    fa[x]=Fa;deep[x]=dep;s[x]=1;
    for(int k=head[x];k;k=nex[k]){
        if(p[k]==Fa)continue;
        dfs(p[k],x,dep+1);s[x]+=s[p[k]];
        if(!son[x]||s[p[k]]>s[son[x]])son[x]=p[k];
    }
}
inline void dfss(int x,int bh){
    top[x]=bh;sx[x]=++ne;xs[sx[x]]=x;
    if(!son[x])return;
    dfss(son[x],bh);
    for(int k=head[x];k;k=nex[k])if(p[k]!=son[x]&&p[k]!=fa[x])dfss(p[k],p[k]);
}
inline void clean(int nod){
    if(add[nod]==0)return;
    if(lt[nod]!=rt[nod]){
        t[nod*2]=t[nod*2]+add[nod]*(rt[nod*2]-lt[nod*2]+1);
        add[nod*2]+=add[nod];
        t[nod*2+1]=t[nod*2+1]+add[nod]*(rt[nod*2+1]-lt[nod*2+1]+1);
        add[nod*2+1]+=add[nod];
    }
    add[nod]=0;
}
inline void build(int l,int r,int nod){
    lt[nod]=l;rt[nod]=r;
    if(l==r){t[nod]=0;return;}
    int mid=l+r>>1;
    build(l,mid,nod*2);build(mid+1,r,nod*2+1);
    t[nod]=t[nod*2]+t[nod*2+1];
}
inline void xg(int i,int j,int nod){
    clean(nod);    
    if(lt[nod]>=i&&rt[nod]<=j){t[nod]+=rt[nod]-lt[nod]+1;add[nod]=1;return;}
    int mid=lt[nod]+rt[nod]>>1;
    if(i<=mid)xg(i,j,nod*2);
    if(j>mid)xg(i,j,nod*2+1);
    t[nod]=t[nod*2]+t[nod*2+1];
}
inline int ssum(int i,int j,int nod){
    clean(nod);
    if(lt[nod]>=i&&rt[nod]<=j)return t[nod];
    int mid=lt[nod]+rt[nod]>>1,ans=0;
    if(i<=mid)ans+=ssum(i,j,nod*2);
    if(j>mid)ans+=ssum(i,j,nod*2+1);
    return ans;
}
inline int fsum(int x,int y){
    int fx=top[x],fy=top[y],ans=0;
    while(fx!=fy){
        if(deep[fx]<deep[fy])swap(fx,fy),swap(x,y);
        ans+=ssum(sx[fx],sx[x],1);
        x=fa[fx];fx=top[x];
    }
    if(deep[x]>deep[y])swap(x,y);
    ans+=ssum(sx[x],sx[y],1);
    return ans;
}
inline void fxg(int x,int y){
    int fx=top[x],fy=top[y];
    while(fx!=fy){
        if(deep[fx]<deep[fy])swap(fx,fy),swap(x,y);
        xg(sx[fx],sx[x],1);
        x=fa[fx];fx=top[x];
    }
    if(deep[x]>deep[y])swap(x,y);
    xg(sx[x],sx[y],1);
}
int main()
{
    scanf("%d",&n);a[0]=1;
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    for(int i=1;i<n;i++){
        int x,y;scanf("%d%d",&x,&y);
        addedge(x,y);addedge(y,x);
    }
    dfs(1,0,1);dfss(1,1);build(1,n,1);
    for(int i=2;i<=n;i++)fxg(a[i-1],a[i]);
    for(int i=1;i<=n;i++)if(a[1]!=i)printf("%d\n",ssum(sx[i],sx[i],1)-1);
    else printf("%d\n",ssum(sx[i],sx[i],1));
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值