Warning: session_start(): open(/tmp/sess_b88a0773a318d6cdcfd49febdbd5f29a, O_RDWR) failed: No space left on device (28) in /data/wiki/inc/init.php on line 239

Warning: session_start(): Failed to read session data: files (path: ) in /data/wiki/inc/init.php on line 239

Warning: Cannot modify header information - headers already sent by (output started at /data/wiki/inc/init.php:239) in /data/wiki/inc/auth.php on line 430

Warning: mkdir(): No space left on device in /data/wiki/lib/plugins/dw2pdf/vendor/mpdf/mpdf/src/Cache.php on line 19
Temporary files directory "/data/wiki/data/tmp/dwpdf/202/" is not writable

Warning: Cannot modify header information - headers already sent by (output started at /data/wiki/inc/init.php:239) in /data/wiki/inc/actions.php on line 38

Warning: Cannot modify header information - headers already sent by (output started at /data/wiki/inc/init.php:239) in /data/wiki/lib/tpl/dokuwiki/main.php on line 12
2020-2021:teams:wangzai_milk:树链剖分 [CVBB ACM Team]

用户工具

站点工具


2020-2021:teams:wangzai_milk:树链剖分

这是本文档旧的修订版!


树链剖分

是什么

怎么用

普通用法

trick | keys:LCA

感觉这两道题有一点点共性但又不是显式的,还是放在一起了,无论如何挺值得两连做的。建议思考,仿佛有智力开发的效果

[BZOJ 3626][LNOI2014]LCA | 线段树

给出$n$个点的有根树,$q$次询问,每次询问给出$l,r,z$,求$\sum\limits_{l\le i\le r}\text{dep}[\text{LCA}(i,z)]$。$1\le n,q\le50000$

题解:定义询问$(x,z)$为$\sum\limits_{1\le i\le x}\text{dep}[\text{LCA}(i,z)]$,则可以将题中询问拆为询问$(r,z)$和$(l-1,z)$的差。于是可以离线,从$1$到$n$逐个加入,加入时将该节点到根节点路径上点的值各加一,线段树维护。而对于每个$z$可以在加入$x$刚好完成时查询$z$到根节点路径上点的值来得到答案。

code:

点击以显示 ⇲

点击以隐藏 ⇱

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define MAXN 50005
#define Mod 201314
using namespace std;
int n,q,deep[MAXN],head[MAXN],cnt=0,tot=0;
int sz=0,maxv[MAXN],father[MAXN],siz[MAXN],top[MAXN],pos[MAXN],res[MAXN*2];
struct Node1
{
    int next,to;
}Edges[MAXN*2];
struct Node2
{
    int l,r,sum,lazy;
}t[MAXN*4];
struct Node3
{
    int x,z,id;
    Node3(int x=0,int z=0,int id=0):x(x),z(z),id(id){}
}Q[MAXN*2];
bool cmp(Node3 a,Node3 b){return a.x<b.x;}
int read()
{
    int x=0,f=1;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    return x*f;
}
void addedge(int u,int v)
{
    Edges[++cnt].next=head[u];
    head[u]=cnt;
    Edges[cnt].to=v;
}
void dfs1(int u)
{
    siz[u]=1;int k=-1;
    for(int i=head[u];~i;i=Edges[i].next)
    {
        int v=Edges[i].to;
        deep[v]=deep[u]+1,dfs1(v);
        siz[u]+=siz[v];
        if(k==-1||siz[v]>siz[k])k=v;
    }
    maxv[u]=k;
}
void dfs2(int u,int t)
{
    ++sz,pos[u]=sz;
    top[u]=t;
    if(maxv[u]!=-1)dfs2(maxv[u],t);
    for(int i=head[u];~i;i=Edges[i].next)
    if(maxv[u]!=Edges[i].to)dfs2(Edges[i].to,Edges[i].to);
}
void build(int idx,int l,int r)
{
    t[idx].l=l,t[idx].r=r;
    if(l==r)return;
    int mid=(l+r)>>1;
    build(idx<<1,l,mid),build(idx<<1|1,mid+1,r);
}
void pushdown(int idx)
{
    if(t[idx].l<t[idx].r&&t[idx].lazy)
    {
        t[idx<<1].lazy+=t[idx].lazy;
        t[idx<<1|1].lazy+=t[idx].lazy;
        t[idx<<1].sum+=t[idx].lazy*(t[idx<<1].r-t[idx<<1].l+1);
        t[idx<<1].sum%=Mod;
        t[idx<<1|1].sum+=t[idx].lazy*(t[idx<<1|1].r-t[idx<<1|1].l+1);
        t[idx<<1|1].sum%=Mod;
        t[idx].lazy=0;
    }
}
void add(int idx,int l,int r)
{
    if(l<=t[idx].l&&r>=t[idx].r)
    {
        t[idx].sum+=t[idx].r-t[idx].l+1;
        t[idx].lazy++;return;
    }
    pushdown(idx);
    int mid=(t[idx].l+t[idx].r)>>1;
    if(r<=mid)add(idx<<1,l,r);
    else if(l>mid)add(idx<<1|1,l,r);
    else add(idx<<1,l,r),add(idx<<1|1,l,r);
    t[idx].sum=(t[idx<<1].sum+t[idx<<1|1].sum)%Mod;
}
int query(int idx,int l,int r)
{
    if(l<=t[idx].l&&r>=t[idx].r)return t[idx].sum;
    pushdown(idx);
    int mid=(t[idx].l+t[idx].r)>>1;
    if(r<=mid)return query(idx<<1,l,r);
    else if(l>mid)return query(idx<<1|1,l,r);
    else return (query(idx<<1,l,r)+query(idx<<1|1,l,r))%Mod;
}
void change(int x)
{
    while(top[x])
    {
        add(1,pos[top[x]],pos[x]);
        x=father[top[x]];
    }
    add(1,pos[top[x]],pos[x]);
}
int ask(int x)
{
    int res=0;
    while(top[x])
    {
        res=(res+query(1,pos[top[x]],pos[x]))%Mod;
        x=father[top[x]];
    }
    res=(res+query(1,pos[top[x]],pos[x]))%Mod;
    return res;
}
int main()
{
    memset(head,-1,sizeof(head));
    n=read(),q=read();
    for(int i=1;i<n;i++){father[i]=read();addedge(father[i],i);}
    deep[0]=1,dfs1(0);
    dfs2(0,0);build(1,1,sz);
    for(int i=1;i<=q;i++)
    {
        int l=read(),r=read(),z=read();
        ++tot,Q[tot]=Node3(l-1,z,tot);
        ++tot,Q[tot]=Node3(r,z,tot);
    }
    sort(Q+1,Q+1+tot,cmp);
    int now=0;
    for(int i=1;i<=tot;i++)
    {
        while(now<=Q[i].x)change(now),++now;
        res[Q[i].id]=ask(Q[i].z);
    }
    for(int i=1;i<=tot;i+=2)
    printf("%d\n",(res[i+1]-res[i]+Mod)%Mod);
    return 0;
}


[BZOJ 4012][HNOI2015]开店 | 主席树

给出$n$个点的带权树,节点$i$的妖怪有年龄$x_i$。$Q$个询问,给出$u,l,r$询问年龄在$[l,r]$的妖怪到$u$点的距离之和,强制在线。$n\le150000,Q\le200000$,年龄上限$A\le10^9$。

题解:首先点$u,v$间的距离可以转换为$\text{dis}(u,\text{root})+\text{dis}(v,\text{root})-\text{dis}(\text{lca}(u,v),\text{root})$。对于每个询问$\text{dis}(u,\text{root})$和$\sum\limits_{x_v\in[l,r]}\text{dis}(v,\text{root})$都是可以通过提前处理快速得到的,那么剩下需要的就是$\sum\limits_{x_v\in[l,r]}\text{dis}(\text{lca}(u,v),\text{root})$。这里就是和上一题有点像的地方了,我们可以将年龄离散化然后维护一个主席树从小到大按顺序加入结点,加入时将该结点到根的边权加到主席树里,之后对于每个询问就也可以直接在$l-1$和$r$查询$u$到根得到结果。但主席树的具体过程是有一点玄妙的,还有lazy tag什么的想象不出可以见代码,$w[i]$维护主席树中结点的父边边权的前缀和。

code:

点击以显示 ⇲

点击以隐藏 ⇱

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define N 150005
#define Q 200005
using namespace std;
typedef long long LL;
int n,q,A,x[N],y[N],head[N],cnt=0;
int father[N],deep[N],pos[N],top[N],siz[N],maxv[N],sz=0;
int rt[N],ls[N*120],rs[N*120],tot=0;
LL sum[N*120],lazy[N*120],dis[N],d[N],w[N],p1[N],p2[N];
LL read()
{
    LL x=0,f=1;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    return x*f;
}
struct Node1
{
    int next,to,w;
}Edges[N*2];
struct Node2
{
    int age,id;
    Node2(int age=0,int id=0):age(age),id(id){}
    bool operator < (const Node2& t) const
    {return age<t.age;}
}data[N];
void addedge(int u,int v,int w)
{
    Edges[++cnt].next=head[u];
    head[u]=cnt;
    Edges[cnt].to=v;
    Edges[cnt].w=w;
}
void dfs1(int u)
{
    siz[u]=1;
    for(int i=head[u];~i;i=Edges[i].next)
    {
        int v=Edges[i].to;
        if(v==father[u])continue;
        father[v]=u;
        deep[v]=deep[u]+1,d[v]=Edges[i].w,dis[v]=dis[u]+Edges[i].w;
        dfs1(v);siz[u]+=siz[v];
        if(!maxv[u]||siz[v]>siz[maxv[u]])maxv[u]=v;
    }
}
void dfs2(int u,int t)
{
    top[u]=t;
    ++sz,pos[u]=sz;
    w[sz]=w[sz-1]+d[u],p1[x[u]]++,p2[x[u]]+=dis[u];
    if(maxv[u])dfs2(maxv[u],t);
    for(int i=head[u];~i;i=Edges[i].next)
    {
        int v=Edges[i].to;
        if(v==maxv[u]||v==father[u])continue;
        dfs2(v,v);
    }
}
void insert(int &idx,int last,int l,int r,int L,int R)
{
    ++tot,idx=tot;
    sum[idx]=sum[last],lazy[idx]=lazy[last],ls[idx]=ls[last],rs[idx]=rs[last];
    if(L==l&&R==r){lazy[idx]++;sum[idx]+=w[r]-w[l-1];return;}
    int mid=(l+r)>>1;
    if(R<=mid)insert(ls[idx],ls[last],l,mid,L,R);
    else if(L>mid)insert(rs[idx],rs[last],mid+1,r,L,R);
    else insert(ls[idx],ls[last],l,mid,L,mid),insert(rs[idx],rs[last],mid+1,r,mid+1,R);
    sum[idx]=sum[ls[idx]]+sum[rs[idx]]+lazy[idx]*(w[r]-w[l-1]);
}
LL query(int s,int t,int l,int r,int L,int R,int tag)
{
    if(L==l&&R==r)return sum[s]-sum[t]+1LL*tag*(w[r]-w[l-1]);
    int mid=(l+r)>>1;tag+=lazy[s]-lazy[t];
    if(R<=mid)return query(ls[s],ls[t],l,mid,L,R,tag);
    else if(L>mid)return query(rs[s],rs[t],mid+1,r,L,R,tag);
    else return query(ls[s],ls[t],l,mid,L,mid,tag)+query(rs[s],rs[t],mid+1,r,mid+1,R,tag);
}
void add(int i,int x)
{
    while(x)
    {
        insert(rt[data[i].age],rt[data[i].age],1,sz,pos[top[x]],pos[x]);
        x=father[top[x]];
    }
}
LL ask(int l,int r,int u)
{
    LL res=0;
    while(u)
    {
        res+=query(rt[r],rt[l-1],1,sz,pos[top[u]],pos[u],0);
        u=father[top[u]];
    }
    return res;
}
int main()
{
    memset(head,-1,sizeof(head));
    n=read(),q=read(),A=read();
    for(int i=1;i<=n;i++)y[i]=x[i]=read();
    sort(y+1,y+1+n);
    int t=unique(y+1,y+1+n)-y-1;
    for(int i=1;i<=n;i++)x[i]=lower_bound(y+1,y+1+t,x[i])-y;
    for(int i=1;i<n;i++)
    {
        int a=read(),b=read(),c=read();
        addedge(a,b,c),addedge(b,a,c);
    }
    dfs1(1);dfs2(1,1);
    for(int i=1;i<=n;i++)data[i]=Node2(x[i],i);
    sort(data+1,data+1+n);
    for(int i=1;i<=n;i++)
    {
        if(!rt[data[i].age])
        rt[data[i].age]=rt[data[i].age-1];
        add(i,data[i].id);
    }
    for(int i=1;i<=t;i++)p1[i]+=p1[i-1],p2[i]+=p2[i-1];
    LL last=0;
    for(int i=1;i<=q;i++)
    {
        LL u=read(),a=read(),b=read();
        int l=min((a+last)%A,(b+last)%A),r=max((a+last)%A,(b+last)%A);
        l=lower_bound(y+1,y+1+t,l)-y,r=upper_bound(y+1,y+1+t,r)-y-1;
        if(l>r||l>t||r<1){last=0;printf("0\n");continue;}
        last=dis[u]*(p1[r]-p1[l-1])+p2[r]-p2[l-1]-2*ask(l,r,u);
        printf("%lld\n",last);
    }
    return 0;
}


2020-2021/teams/wangzai_milk/树链剖分.1590339806.txt.gz · 最后更改: 2020/05/25 01:03 由 zars19