树链剖分是极其恶心的要用到其他数据结构的一种数据结构(或者说处理策略)。在应用之前,需要熟练掌握树形Dp,Dfs,Dfs序,对数相关概念,线段树/Splay/…。这道题就要用到树链剖分。

题意

给定一棵n节点树(n<=5×10),和q次操作(q<=105),操作有两种:1.将连接a,b的链上的所有节点的权值加c;2.求点a的权值。有多组数据。

思路

由于LCA我们最快可以O(1)求,但是更新权值需要O(n)的复杂度。那么暴力算法的复杂度就是O(qn),不再可承受范围内。
所以我们有这么几个思路:
1.讨论如何用数据结构优化区间修改操作。
2.讨论如何在快速求出LCA的同时还可以快速更新。
于是傻×都可以看出来这个要用树链剖分。具体神码是树链剖分详见本博客的另一篇教程。
简单来讲,我们为了可以比O(n)快地求LCA并且可以成段更新,我们将树划分成两种颜色不同的边。同种颜色的相连边可以保存深度最小的点是谁,于是就可以跳跃着求出LCA,过程与倍增类似。通过良好的划分方式,我们可以得到一个双色的树,这棵树的每一个点到根节点只需要跳跃logn级别的次数。对于每一次跳跃跳过的链,我们可以依次将链上的节点编号,放入线段树中。每一条边的节点在线段树中是连续的。所以可以logn修改。这样我们就得到了O(nlognlogn)的解法。

#include <iostream>
#include <cstring>
#include <cstdio>

#define MX 100009
#define ls (node<<1)
#define rs ((node<<1)+1)
#define mid ((l+r)>>1)

using namespace std;

int siz[MX];                                                        //子树大小
int top[MX];                                                      //重链(其中一种颜色的连续边)的顶端节点
int son[MX];                                                      //重儿子(详见教程)
int dep[MX];                                                     //每个点的深度
int fa[MX];                                                         //父节点
int tid[MX];                                                        //节点编号
int rak[MX];                                                       //编号对应的节点
int fst[MX],nxt[MX*2],v[MX*2],lnum=-1;
int n,m,p,w[MX],tim;

void addeg(int nu,int nv)
{
    lnum++;
    nxt[lnum]=fst[nu];
    fst[nu]=lnum;
    v[lnum]=nv;
}

void dfs1(int x,int fat,int d)
{
    dep[x]=d;
    fa[x]=fat;
    siz[x]=1;
    for(int i=fst[x];i!=-1;i=nxt[i])
    {
        if(v[i]==fat)continue;
        dfs1(v[i],x,d+1);
        siz[x]+=siz[v[i]];
        if(son[x]==-1||siz[v[i]]>siz[son[x]])son[x]=v[i];
    }
}

void dfs2(int x,int tp)
{
    top[x]=tp;
    tid[x]=++tim;
    //cout<<"set:"<<x<<" "<<tid[x]<<endl;
    rak[tid[x]]=x;
    if(son[x]==-1)return;
    dfs2(son[x],tp);
    for(int i=fst[x];i!=-1;i=nxt[i])
    {
        if(v[i]==son[x]||v[i]==fa[x])continue;
        dfs2(v[i],v[i]);
    }
}

typedef struct tnode
{
    int sm,l,r,x;
}treenode;
treenode tree[MX*4];

void build(int node,int l,int r)
{
    tree[node].sm=0;
    tree[node].l=l;
    tree[node].r=r;
    if(l==r)tree[node].x=w[rak[l]];
    else build(ls,l,mid),build(rs,mid+1,r);
}

void pushdown(int node)
{
    tree[node].x+=tree[node].sm;
    tree[ls].sm+=tree[node].sm;
    tree[rs].sm+=tree[node].sm;
    tree[node].sm=0;
}

void add(int node,int ql,int qr,int x)
{
    pushdown(node);
    int l=tree[node].l,r=tree[node].r;
    if(l>=ql&&r<=qr)
    {
        tree[node].sm+=x;
        pushdown(node);
    }
    else if(l>qr||r<ql)return ;
    else add(ls,ql,qr,x),add(rs,ql,qr,x);
}

int qsm(int node,int qp)
{
    pushdown(node);
    int l=tree[node].l,r=tree[node].r;
    if(l==r)return tree[node].x;
    else if(qp<=mid)return qsm(ls,qp);
    else return qsm(rs,qp);
}

void change(int s,int t,int x)
{
    int f1=top[s],f2=top[t];
    while(f1!=f2)
    {
        if(dep[f1]<dep[f2])swap(f1,f2),swap(s,t);
        add(1,tid[f1],tid[s],x);
        s=fa[f1];
        f1=top[s];
    }
    if(dep[s]>dep[t])swap(s,t);
    add(1,tid[s],tid[t],x);
}

void init()
{
    memset(fst,0xff,sizeof(fst));
    memset(son,0xff,sizeof(son));
    lnum=-1;
    tim=0;
}

void input()
{
    int a,b;
    for(int i=1;i<=n;i++)scanf("%d",&w[i]);
    for(int i=1;i<=m;i++)scanf("%d%d",&a,&b),addeg(a,b),addeg(b,a);
}

void work()
{
    int a,b,c;
    char com[10];
    for(int i=1;i<=p;i++)
    {
        scanf("%s",com);
        if(com[0]=='I'){scanf("%d%d%d",&a,&b,&c);change(a,b,c);}
        else if(com[0]=='D'){scanf("%d%d%d",&a,&b,&c);change(a,b,-c);}
        else if(com[0]=='Q'){scanf("%d",&a);printf("%d\n",qsm(1,tid[a]));}
    }
}

int main()
{
    while(~scanf("%d%d%d",&n,&m,&p))
    {
        init();
        input();
        dfs1(1,0,1);
        dfs2(1,1);
        build(1,1,n);
        work();
    }

    return 0;
}

分类: 文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

你是机器人吗? =。= *