树链剖分(SPOJ-QTREE)(模板)

先推荐一个菊苣的博客讲解 。

对于这种查询树链,修改树边的问题,我们可以使用树链剖分。

首先处理出重边和轻边:

u son[u] 为重边,当且仅当 son[u] u 的所有点中,其子树点数最多的。

②不是重边的边为轻边。

由重边形成的链为重链。

性质:

①从根节点到任意点的轻边数量为 O(logn)

证明:轻边的两端点的子树点数之差至少为2倍。

②从根节点到任意点的重边数量为 O(logn)

证明:轻边和重链是交错的,因此其数量之差至多为 1

处理出轻边和重链可以通过两次 dfs 实现。

第一次处理出重边和轻边,第二次处理出重链。

将每条重链维护在线段树上,这样就可以方便查询了。

对于每一次查询(如查询两点之间路径上的最大值):

①如果两点在同一条重链上,可以直接通过线段树查询,每次查询的时间复杂度是 O(logn) (好像网上的题解一般都是讲所有重链维护在一棵线段树上,我觉得可以对每条重链维护一棵线段树。。。不过可能优化不了多少)。

②如果两点不在同一条重链上,我们可以通过类似倍增的方法将两点向上提,从而使其在同一条重链上,这样就转到了①,复杂度是 O(log2n)

对于每一次修改:

①如果修改的是轻边,直接修改就行,复杂度是 O(1)

①如果修改的是重边,则直接用线段树就行,复杂度是 O(logn)

因此树链剖分的复杂度是 O(nlogn+qnlog2n)

#include
using namespace std;
const int N=1e4+7;
int sz[N],fa[N],top[N],son[N],d[N],id[N],n,cnt,lt[N];
vector<int> adj[N];
struct Edge
{
    int u,v,c;
}es[N];
int tree[N<<2];
void dfs1(int u, int father, int deep)
{
    d[u]=deep;
    sz[u]=1;
    fa[u]=father;
    for(int i=0;iint v=adj[u][i];
        if(v!=father)
        {
            dfs1(v,u,deep+1);
            sz[u]+=sz[v];
            if(son[u]==-1||sz[v]>sz[son[u]]) son[u]=v;
        }
    }
}

void dfs2(int u)
{
    id[u]=++cnt;
    if(son[u]!=-1)
    {
        top[son[u]]=top[u];
        dfs2(son[u]);
    }
    for(int i=0;iint v=adj[u][i];
        if(v!=fa[u]&&v!=son[u])
        {
            top[v]=v;
            dfs2(v);
        }
    }
}

void update(int rt, int l, int r,int q, int val )
{
    if(l==q&&r==q)
    {
        tree[rt]=val;
        return ;
    }
    int mid=(l+r) >> 1;
    if(q <= mid)  update(rt<<1, l, mid, q, val);
    else update(rt<<1|1, mid+1, r, q , val);
    tree[rt]=max(tree[rt<<1],tree[rt<<1|1]);
}

int query(int rt,int l,int r,int ql,int qr)
{
    if(l>=ql&&r<=qr)
    {
        return tree[rt];
    }
    int mid=(l+r)>>1;
    if(ql>mid) return query(rt<<1|1,mid+1,r,ql,qr);
    if(qr<=mid) return query(rt<<1,l,mid,ql,qr);
    return max(query(rt<<1,l,mid,ql,qr),query(rt<<1|1,mid+1,r,ql,qr));
}

void init()
{
    cnt=0;
    memset(son,-1,sizeof(son));
    for(int i=1;i<=n;i++) adj[i].clear();
    top[1]=1;
}

int main()
{
    int T;
    scanf("%d", &T);
    while (T--)
    {
        scanf("%d",&n);
        init();
        int u,v,c;
        for(int i=1;iscanf("%d%d%d",&u,&v,&c);
            es[i].u=u;
            es[i].v=v;
            es[i].c=c;
            adj[u].push_back(v);
            adj[v].push_back(u);
        }
        dfs1(1,0,1);
        dfs2(1);
        for(int i=1;iint u=es[i].u,v=es[i].v;
            if(top[u]==top[v])
            {
                if(d[u]>d[v]) swap(u,v);
                update(1,1,n,id[u],es[i].c);
            }
            else
            {
                if(d[u]char s[10];
        while(~scanf("%s",s))
        {
            if(s[0]=='D') break;
            if(s[0]=='Q')
            {
                int u,v;
                scanf("%d%d",&u,&v);
                int ans=0;
                while(top[u]!=top[v])
                {
                    if(d[top[u]]if(u!=top[u])
                        ans=max(ans,query(1,1,n,id[top[u]],id[u]-1));
                    u=top[u];
                    ans=max(ans,lt[u]);
                    u=fa[u];
                }
                if(d[u]>d[v]) swap(u,v);
                if(u!=v) ans=max(ans,query(1,1,n,id[u],id[v]-1));
                printf("%d\n",ans);
            }
            else
            {
                int x,c;
                scanf("%d%d",&x,&c);
                int u=es[x].u,v=es[x].v;
                if(d[u]>d[v]) swap(u,v);
                if(top[u]==top[v])
                    update(1,1,n,id[u],c);
                else lt[v]=c;
            }
        }
    }
    return 0;
}

你可能感兴趣的:(模板,树链剖分)