E - 树上的距离

Time Limit: 2000/1000MS (Java/Others)     Memory Limit: 262143/262143KB (Java/Others)
 

给你一棵带权树,边权表示两点间的距离。有如下两种操作:

  1. 询问两点间的最短距离。
  2. 修改一条边权值。

对每个操作1,回答两点间的距离。

Input

第一行一个数n,表示点的个数。接下来n1行,每行3个数uvw,表示uv间有权为w的边。边按照输入顺序从1n1编号。输入保证为一颗树。 (2n1051u,vn1w1000

之后输入一个数q,表示询问个数。最后有q行,每行第一个数opop=12)为操作类型。

op=1时,输入uv表示询问uv间的距离。 (uv1u,vn

op2时,输入idw表示把编号为id的边的权值改为w。 (1q1051idn11w1000

Output

对于对每个操作1,输出一行,回答两点间的距离。

Sample input and output

Sample InputSample Output
5
1 2 2
2 3 3
1 5 1
2 4 4
5
1 1 3
1 2 5
2 1 1
2 3 3
1 2 5
5
3
4

 

解题报告:

这是一道 LCA + 欧拉序列 + 线段树题目

首先我们容易得出下列这个式子

设 distance(x) 为树上的点 x 到根的距离,设u为x,y的最近公共祖先

则distance of (x – y) = distance(x) + distance(y) – 2*distance(u)

那么我们该如何求得LCA呢

这里我采用的是tarjan离线处理所有询问.

一遍dfs处理所有询问的LCA,复杂度为O(n+q)

之后我们用一次dfs将树转为线性结构.

即记录这个点的入时间戳,也记录这个点的出时间戳.

容易得到[ 入时间戳 + 1 , 出时间戳 ] 恰好包含了这个点及其儿子

这样,本题就转换成了线段树区间更新 + 点查询的题目

 

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <vector>
#define pb push_back
using namespace std;
const int maxn = 2e5 + 20;

typedef struct Edge
{
 int u,v,w;
 Edge(int u,int v,int w)
  {
     this->u = u , this->v = v , this->w = w ;     
  }    
};

typedef struct treenode
{
  int l,r,lazy,sum;
  void updata(int x)
   {
       lazy += x;
       sum += (r-l+1)*x;    
   }    
};

typedef struct querynode
{
  int type,t1,t2,lca;
};

typedef struct tarjan
{
  int v , ansid;
  tarjan(int v,int ansid)
   {
         this->v = v ,this->ansid = ansid;
   }
};


treenode tree[maxn*8];


void build_tree(int o,int l,int r)
{
   tree[o].l = l , tree[o].r = r ; tree[o].sum = tree[o].lazy = 0;
   if (r > l)
    {
        int mid = l + (r-l)/2;
        build_tree(2*o,l,mid);
        build_tree(2*o+1,mid+1,r);
    }
}

inline void push_up(int x)
{
   tree[x].sum = tree[2*x].sum + tree[2*x+1].sum;
}

inline void push_down(int x)
{
   int lazy = tree[x].lazy;
   if (lazy)
    tree[2*x].updata(lazy),tree[2*x+1].updata(lazy);
   tree[x].lazy = 0;
}

void updata(int ql,int qr,int o,int v)
{
   int l = tree[o].l , r = tree[o].r;
   if (ql <= l && qr >= r )
    tree[o].updata(v);
   else
    {
        int mid = l + (r-l)/2;
        push_down(o);
        if (mid >= ql)
         updata(ql,qr,2*o,v);
        if (mid < qr)
         updata(ql,qr,2*o+1,v);
        push_up(o);
    }
}

int querysum(int ql,int qr,int o)
{
   int l = tree[o].l , r = tree[o].r;
   if (ql <= l && qr >= r)
    return tree[o].sum;
   else
    {
        int mid = l + (r-l)/2;
        push_down(o);
        int res = 0;
        if (mid >= ql)
         res += querysum(ql,qr,2*o);
        if (mid < qr)
         res += querysum(ql,qr,2*o+1);
        push_up(o);
        return res;
    }
}

int n,euler[maxn*2],id[maxn][2],sum[maxn*2],deapth[maxn*2],tot = 1,pre[maxn],father[maxn];
vector<Edge>New_next[maxn],e;
bool vis[maxn];
querynode q[maxn];
vector<tarjan>use[maxn];

inline int find_set(int x)
{
   return x != pre[x] ? pre[x] = find_set(pre[x]) : x;
}

inline void union_set(int x,int y)
{
   pre[find_set(x)] = y;
}

void init_id(int cur)
{
   id[cur][0] = tot++;
   for(int i = 0 ; i < New_next[cur].size() ; ++ i)
    {
        int nextnode = New_next[cur][i].v;
        if (nextnode == father[cur])
         continue;
        init_id(nextnode);
    }
   id[cur][1] = tot++;
}

void init_tarjan(int cur)
{
   pre[cur] = cur;
   vis[cur] = true;
   for(int i = 0 ; i < New_next[cur].size() ; ++ i)
    {
        int nextnode = New_next[cur][i].v;
        if (nextnode == father[cur])
         continue;
        init_tarjan(nextnode);
        union_set(nextnode,cur);
    }
   for(int i = 0 ; i < use[cur].size() ; ++ i)
    {
        int v = use[cur][i].v , ansid = use[cur][i].ansid;        
        if (vis[v])
         q[ansid].lca = find_set(v);
    }
}

void fatherset(int cur,int fat)
{
   father[cur] = fat;
   for(int i = 0 ; i < New_next[cur].size() ; ++ i)
    {
        int nextnode = New_next[cur][i].v;
        if (nextnode == fat)
         continue;
        fatherset(nextnode,cur);
    }
}



int main(int argc,char *argv[])
{
  scanf("%d",&n);
  memset(sum,0,sizeof(sum));
  memset(vis,false,sizeof(vis));
  for(int i = 0 ; i < n - 1 ; ++ i)
   {
         int u,v,w;
         scanf("%d%d%d",&u,&v,&w);
         e.pb(Edge(u,v,w));
         New_next[u].pb(Edge(u,v,w));
         New_next[v].pb(Edge(v,u,w));
   }
  fatherset(1,0);
  init_id(1);
  build_tree(1,1,tot+5);
  for(int i = 0 ; i < e.size() ; ++ i)
   {
         int u = e[i].u , v = e[i].v , w = e[i].w;
         if (father[u] == v)
          {
             swap(u,v);
             swap(e[i].u,e[i].v);
       }
         updata(id[v][0]+1,id[v][1],1,w);
   }
  int qnumber;
  scanf("%d",&qnumber);
  for(int i = 0 ; i < qnumber ; ++ i)
   {
         int type,t1,t2;
         scanf("%d%d%d",&type,&t1,&t2);
         q[i].type = type,q[i].t1 = t1 , q[i].t2 = t2;
         if (type & 1)
          {
                if (father[t1] == t2)
              swap(q[i].t1,q[i].t2);
                use[t1].pb(tarjan(t2,i));
                use[t2].pb(tarjan(t1,i));
       }
   }
  init_tarjan(1); //离线处理
  for(int i = 0 ; i < qnumber ; ++ i)
   {
         int type = q[i].type ,t1 = q[i].t1 ,t2 = q[i].t2 ;
         if (type & 1)
          {
                int lca = q[i].lca;
                int p1 = querysum(id[t1][1],id[t1][1],1);
                int p2 = querysum(id[t2][1],id[t2][1],1);      
                int p3 = querysum(id[lca][1],id[lca][1],1);
                printf("%d\n",p1+p2-2*p3);
       }
      else
       {
             int u = e[t1-1].u, v = e[t1-1].v;
             updata(id[v][0]+1,id[v][1],1, t2 - e[t1-1].w);
             e[t1-1].w = t2;
       }
   }
  return 0;
}

 

posted on 2015-06-12 01:03  天心散人  阅读(301)  评论(0编辑  收藏  举报