「学习笔记」[POI2014] bzoj 4543 Hotel加强版 - 长链剖分 - 学习笔记

长链剖分定义是把最大子树改为最大深度(下文沿别的blog仍然称为重儿子)。

第一个应用是要求O(nlgn+q)的时间复杂度内求k级祖先(并且强制在线)。做法是长链剖分有这样一个性质:一个点x的k级祖先y,从y走到链底长度不小于k,这个显然。现在要求x的k级祖先,做法是对每个点维护朴素倍增算法的倍增数组,以及对每条链的链顶(假设这条链长度是len)维护沿这条链向下len步和向上len步共O(2len)个答案。每次询问首先找一个最大的t满足 2 t k ,然后用倍增数组求出从当前这个点跳 2 t 步的答案y。然后还要从y跳 r = k 2 t < 2 t 布,根据上问性质,y所在链长度不小于 2 t ,也就是不小于r;因此直接利用刚刚的信息输出即可。

第二个应用是用他来做一些类似与dsu on tree的东西,不过这类问题的显著特征是其与深度的关系更大而与子树大小的关系略浅。例如多次询问一颗树中x的子树中和x距离k的点有多少,那么记做ans[x,k],若令y表示x的儿子节点,则 a n s [ x , k + 1 ] = y a n s [ y , k ] ,直接做就gg了,考虑每次我们直接把重儿子的数组位移一下当成当前的数组,这样被证明复杂度是线性的。当然有些时候这个数组也会被替换成别的例如线段树啊之类的。

例子是这个bzoj 4543。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define N 100010
#define gc getchar()
#define lint long long
using namespace std;
inline int inn()
{
    int x,ch;while((ch=gc)<'0'||ch>'9');
    x=ch^'0';while((ch=gc)>='0'&&ch<='9')
        x=(x<<1)+(x<<3)+(ch^'0');return x;
}
struct edges{
    int to,pre;
}e[N<<1];int h[N],etop,d[N],l[N],son[N];
inline int add_edge(int u,int v) { return e[++etop].to=v,e[etop].pre=h[u],h[u]=etop; }
int get_pos(int x,int fa=0)
{
    d[x]=d[fa]+1,l[x]=0,son[x]=0;
    for(int i=h[x],y;i;i=e[i].pre)
        if((y=e[i].to)^fa)
        {
            l[x]=max(l[x],get_pos(y,x));
            if(l[y]>l[son[x]]) son[x]=y;
        }
    return l[x]+1;
}
lint ans=0ll;
int getans(int x,int fa,int *fx,lint *gx)
{
    if(son[x]) getans(son[x],x,fx+1,gx-1),ans+=gx[0];
    fx[0]=1,gx[l[x]]=gx[l[x]-1]=0;//?
    for(int i=h[x],y;i;i=e[i].pre)
        if((y=e[i].to)!=fa&&e[i].to!=son[x])
        {
            int ly=l[y]+1,*fy=new int[ly];
            lint *gy=new lint[ly<<1]+ly;getans(y,x,fy,gy);
            for(int j=0;j<=l[y];j++)
                ans+=fy[j]*gx[j+1]+(j?gy[j]*fx[j-1]:0);
            for(int j=0;j<=l[y];j++)
                (j?gx[j-1]+=gy[j]:0),gx[j+1]+=(lint)fy[j]*fx[j+1];
            for(int j=0;j<=l[y];j++) fx[j+1]+=fy[j];
        }
    return 0;
}
int main()
{
    int n=inn();
    for(int i=1,u,v;i<n;i++) u=inn(),v=inn(),add_edge(u,v),add_edge(v,u);
    get_pos(1),getans(1,0,new int[l[1]+1],(new lint[(l[1]+1)<<1])+l[1]+1);
    return !printf("%lld\n",ans);
}

猜你喜欢

转载自blog.csdn.net/Mys_C_K/article/details/82260998