UESTC 912 树上的距离 --LCA+RMQ+树状数组

1.易知,树上两点的距离dis[u][v] = D[u]+D[v]-2*D[lca(u,v)] (D为节点到根节点的距离)

2.某条边<u,v>权值一旦改变,将会影响所有以v为根的子树上的节点到根节点的距离,很明显,DFS一遍后以v为根的子树在DFS序列中是连续的一段,及转化为区间更新问题,可以用树状数组。

做法:先把求LCA解决,LCA可以转化为RMQ问题,可参见:LCA转RMQ, 即转化为LCA(T,u,v) = RMQ(B,pos[u],pos[v]),其中B为深度序列。预先DFS可以处理出深度序列,我这里用的是“另一种”深度序列,即时间戳表示的深度序列,用时间戳来代表深度,以及欧拉序列和pos数组,还有in[],out[]数组,表示每个节点DFS进出的时间戳。在DFS时间戳序列上建树状数组,值为每个节点与原来到根节点的距离相比的该变量。要用树状数组需要用一个巧妙的方法转为前缀和问题:在时间戳序列中,如果要更新v的子树,即为更新in[v],out[v],令a = in[v],b=out[v],则可以令A[a] = delta,A[b+1] = -delta。然后更新,就可以用树状数组来查询了。然后每次求两点间距离的时候,结果为固定的dis[u][v]+变动的值(树状数组query求出)。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define N 100007

struct Edge
{
    int u,v,w,next;
}G[2*N];

int first[N],tot,n,Time,ind,ola[3*N],id;
int pos[N],in[2*N],out[2*N],vis[3*N];
int d[2*N][24],dis[N],c[2*N],T[2*N],dp[3*N];

void RMQ_init()
{
    int i,j;
    for(i=1;i<=id;i++)
        d[i][0] = dp[i];
    for(j=1;(1<<j)<=id;j++)
    {
        for(i=1;i+(1<<j)-1<=id;i++)
            d[i][j] = min(d[i][j-1],d[i+(1<<(j-1))][j-1]);
    }
}

int RMQ(int l,int r)
{
    int k = 0;
    while((1<<(k+1)) <= r-l+1)
        k++;
    return min(d[l][k],d[r-(1<<k)+1][k]);
}

void addedge(int u,int v,int w)
{
    G[tot].u = u;
    G[tot].v = v;
    G[tot].w = w;
    G[tot].next = first[u];
    first[u] = tot++;
}

void init()
{
    memset(G,0,sizeof(G));
    memset(first,-1,sizeof(first));
    memset(c,0,sizeof(c));
    memset(d,0,sizeof(d));
    tot = 1;
    Time = 0;
    ind = 0;
    id = 0;
}

void DFS(int u,int fa)
{
    ++Time;
    int deep = Time;
    ola[++ind] = u;
    T[Time] = u;
    dp[++id] = Time;
    in[u] = Time;
    for(int i=first[u];i!=-1;i=G[i].next)
    {
        int v = G[i].v;
        if(v == fa)
            continue;
        DFS(v,u);
        dp[++id] = deep;  //深度序列
        ola[++ind] = u;   //欧拉序列
    }
    //if(flag)
        //ola[++ind] = u;
    out[u] = Time;
}

void DFSWG(int u,int w,int fa)
{
    dis[u] = w;
    for(int i=first[u];i!=-1;i=G[i].next)
    {
        int v = G[i].v;
        if(v == fa)
            continue;
        DFSWG(v,w+G[i].w,u);
    }
}

void Getpos()
{
    memset(vis,0,sizeof(vis));
    for(int i=1;i<=ind;i++)
    {
        if(!vis[ola[i]])
        {
            vis[ola[i]] = 1;
            pos[ola[i]] = i;
        }
    }
}

int LCA(int u,int v)
{
    int L = min(pos[u],pos[v]);
    int R = max(pos[u],pos[v]);
    return T[RMQ(L,R)];
}

int lowbit(int x)
{
    return x&(-x);
}

void update(int k,int num)
{
    while(k <= n)
    {
        c[k] += num;
        k += lowbit(k);
    }
}

int query(int k)
{
    int sum = 0;
    while(k > 0)
    {
        sum += c[k];
        k -= lowbit(k);
    }
    return sum;
}

int main()
{
    int i,j,u,v,w,op,q;
    scanf("%d",&n);
    {
        init();
        for(i=0;i<n-1;i++)
        {
            scanf("%d%d%d",&u,&v,&w);
            addedge(u,v,w);
            addedge(v,u,w);
        }
        scanf("%d",&q);
        DFS(1,-1);
        Getpos();
        RMQ_init();
        DFSWG(1,0,-1);  //算出dis
        while(q--)
        {
            scanf("%d%d%d",&op,&u,&v);
            if(op == 1)
            {
                int lca = LCA(u,v);
                printf("%d\n",dis[u]+dis[v]-2*dis[lca]+query(in[u])+query(in[v])-2*query(in[lca]));
            }
            else
            {
                int s = G[2*u-1].u;
                int t = G[2*u-1].v;
                if(pos[s] > pos[t])  //保证s是t的父亲
                    swap(s,t);
                int delta = v - G[2*u-1].w;
                G[2*u-1].w = G[2*u].w = v;  //更新权值
                update(in[t],delta);
                update(out[t]+1,-delta);
            }
        }
    }
    return 0;
}

UESTC 912 树上的距离 --LCA+RMQ+树状数组,布布扣,bubuko.com

时间: 2024-10-10 17:53:59

UESTC 912 树上的距离 --LCA+RMQ+树状数组的相关文章

HDU - 6393 Traffic Network in Numazu (LCA+RMQ+树状数组)

这道题相当于将这两题结合: http://poj.org/problem?id=2763 http://codeforces.com/gym/101808/problem/K 题意:有N各点N条边的带权无向图(相当于一棵树多了一条边),两种操作:修改一条边的权值:求两点间的最短路径. 分析:将任意一条边取出来,其余n-1条边可以结合LCA解最短路.询问时,比较通过取出的边和仅通过树上的边的路径的大小,最小值就是两点的最短路径. 树状数组差分维护点到根节点的距离,根据dfs序来记录需要维护的范围.

RMQ &amp;&amp; 树状数组 (初学)

先复习一下今天刚学的RMQ算法知识; RMQ算法(Range Minimum Query) :1.算法思想 求静态范围最值问题,适合于静态连续区间查询. A[ i ] [ j ] 的值代表的是原数组中以 i 开始的连续 (1<< j)个数的最值. 2.代码 <pre name="code" class="cpp">//2.1 预处理代码 for(int j = 1 ; j != 20 ; ++j ) // 代表区间大小 for(int i =

【BZOJ】1047: [HAOI2007]理想的正方形(单调队列/~二维rmq+树状数组套树状数组)

http://www.lydsy.com/JudgeOnline/problem.php?id=1047 树状数组套树状数组真心没用QAQ....首先它不能修改..而不修改的可以用单调队列做掉,而且更快,只有O(n^2).而这货是n^2log^2n的建树...虽然查询是log^2n...但是建树那里就tle了.. 那么说题解... 先orz下,好神.. 我怎么没想到单调队列orz 首先我们维护 行 的单调队列,更新每个点在 列 距离内的最小and最大的值 然后我们维护 列 的单调队列,更新每个点

hdu5293 lca+dp+树状数组+时间戳

题意是给了 n 个点的树,会有m条链条 链接两个点,计算出他们没有公共点的最大价值,  公共点时这样计算的只要在他们 lca 这条链上有公共点的就说明他们相交 dp[i]为这个点包含的子树所能得到的最大价值 sum[i]表示这个点没有选择经过i这个点链条的总价值 两种选择 这个点没有被选择 dp[i]=sum[i]=sigma(dp[k])k为i的子树 选择了某个链 假设这条链 为(tyuijk) 那么dp[i]=(sum[i]-dp[u]-dp[j])+(sum[j]-dp[k])+dp[k]

BZOJ 2780: [Spoj]8093 Sevenk Love Oimaster( 后缀数组 + 二分 + RMQ + 树状数组 )

全部串起来做SA, 在按字典序排序的后缀中, 包含每个询问串必定是1段连续的区间, 对每个询问串s二分+RMQ求出包含s的区间. 然后就是求区间的不同的数的个数(经典问题), sort queries + BIT 就行了.时间复杂度O(N log N). 速度垫底了QAQ 你们都会SAM.... ---------------------------------------------------------------------- #include<cmath> #include<c

[NOI2011][bzoj2434] 阿狸的打字机 [AC自动机+dfs序+fail树+树状数组]

题面 传送门 正文 最暴力的 最暴力的方法:把所有询问代表的字符串跑一遍kmp然后输出 稍微优化一下:把所有询问保存起来,把模板串相同的合并,求出next然后匹配 但是这两种方法本质没有区别,都是暴力 不那么暴力的 我们对于所有的串建立一个AC自动机,把询问按照$y$排序,然后在AC自动机上面跑,每次跳fail更新答案 这样可以拿到70分,但是时间上限还是会$O\left(n^2\right)$左右 巧妙的优化 这道题里面,所有的模板串和文本串都在AC自动机里 那么,题目中实际是在要求什么呢?

UVA 11990 ”Dynamic“ Inversion(线段树+树状数组)

[题目链接] UVA11990 [题目大意] 给出一个数列,每次删去一个数,求一个数删去之前整个数列的逆序对数. [题解] 一开始可以用树状数组统计出现的逆序对数量 对于每个删去的数,我们可以用线段树求出它在原序列中的逆序对贡献 在线段树的每个区间有序化数据,就可以二分查找出这个数在每个区间的位置, 这样就处理出了划分出的区间的贡献,先用答案减去这一部分 接下来考虑已经删去部分的容斥,我们发现只要对删去部分再做一次类似的操作, 将这一部分跟当前删去数求一次贡献就是刚才多减去的部分,将这部分的答案

HDU 5458 Stability(双连通分量+LCA+并查集+树状数组)(2015 ACM/ICPC Asia Regional Shenyang Online)

题目大意:给一个N个点M条边的无向图,有Q个询问:1.删掉a.b之间所存在的边:2.询问有多少条边,单独删掉之后a与b不再连通. 思路:脑洞大开. 对于询问,首先想到的就是a与b之间有多少桥(割边),然后想到双连通分量,然而删边是个坑爹的问题,于是我们离线倒着来,把删边变成加边. 双连通分量这种东西呢,其实缩点连起来之后,就是一棵树辣. 然后询问两个点的时候,设根到点x的距离为dep[x],a.b的最近公共祖先为lca(a, b),那么询问query(a, b) = dep[a] + dep[b

Hdu 5458 Stability (LCA + 并查集 + 树状数组 + 缩点)

题目链接: Hdu 5458 Stability 题目描述: 给出一个还有环和重边的图G,对图G有两种操作: 1 u v, 删除u与v之间的一天边 (保证这个边一定存在) 2 u v, 查询u到v的路径上有几条桥. 解题思路: 这个题目有很多次操作,包含查询和删边两类,首先想到的是连通分量加缩点.如果按照顺序来,删边时候求桥就是问题了.所以可以离线处理,然后一边记录答案一边加边缩点. 对于一个图,把连通分量缩成一个点后,这个图就成为了一棵树, 然后深度差就等于桥的数目.查询的时候对于(u, v)