poj 2763(RMQ+BIT\树链剖分)

传送门:Problem 2763

https://www.cnblogs.com/violet-acmer/p/9686774.html

 

题意:

  一对夫妇居住在xx村庄,小屋之间有双向可达的道路,不会出现环,即所构成的图是个树,从ai小屋到bi小屋需要花费wi时间,一开始,女主角在s小屋,有两个询问,

  ①0 u : 她又个孩子在u屋,需要妈妈接她回家,输出从s到u所需的最短时间。

  ②1 x val : 由于种种原因,第x条道路的行走时间由之前的w[x]变为了val。

题解:

  (1)RMQ+BIT

    因为树中连接两点的路径是唯一的,如果我们对顶点进行合理排列的话,能否像链状时那样,进行类似的处理呢?

    考虑利用RMQ计算LCA时所用的,按DFS访问的顺序排列顶点序列。

    这样,u和v之间的路径,就是在序列中u 和 v 之间的所有边减去往返重复的部分得到的结果。

 

 

                                       

    于是,只要令边的权重沿叶子方向为正,沿根方向为负,那么往返重复的部分就自然抵消了,于是有

        (u,v之间的花费的时间)=(从LCA(u,v)到u的花费的时间和)+(从LCA(u,v)到v的花费的时间和);

    同链状情况一样,利用BIT的话,计算权重和更新边权都可以在O(logn)时间内办到,而LCA也能够在O(longn)时间内求得。

  (2)树链剖分

AC代码:

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cmath>
  4 #include<cstring>
  5 #include<vector>
  6 using namespace std;
  7 #define pb push_back
  8 #define mem(a,b) (memset(a,b,sizeof a))
  9 #define lowbit(x) (x&(-x))
 10 const int maxn=1e5+50;
 11 
 12 int n,q,s;
 13 int w[maxn];//存储第 i 条边的权值
 14 struct Node
 15 {
 16     int to;
 17     int w;
 18     int id;
 19     Node(int to,int w,int id):to(to),w(w),id(id){}
 20 };
 21 vector<Node >G[maxn];
 22 void addEdge(int u,int v,int cost,int id)
 23 {
 24     G[u].pb(Node(v,cost,id));
 25     G[v].pb(Node(u,cost,id));
 26 }
 27 int vs[2*maxn];//欧拉序列
 28 int depth[2*maxn];//深度序列
 29 int id[maxn];//id[i] : 记录节点 i 在欧拉序列中第一次出现的位置
 30 int es[2*maxn];//边的下标,i*2 : 叶子方向 i*2+1 : 根方向
 31 int total;//记录欧拉序列的下标总个数,其实最终的 total = 2*n
 32 //================BIT==================
 33 int bit[2*maxn];//树状数组
 34 void Add(int x,int val)
 35 {
 36     while(x < 2*maxn)
 37     {
 38         bit[x] += val;
 39         x += lowbit(x);
 40     }
 41 }
 42 int Sum(int x)
 43 {
 44     int sum=0;
 45     while(x > 0)
 46     {
 47         sum += bit[x];
 48         x -= lowbit(x);
 49     }
 50     return sum;
 51 }
 52 //=====================================
 53 //==================RMQ================
 54 struct RMQ
 55 {
 56     int dp[20][2*maxn];
 57     void Init(){
 58         for(int i=0;i < 2*maxn;++i)
 59             dp[0][i]=i;
 60     }
 61     void ST()
 62     {
 63         int k=log(total)/log(2);
 64         for(int i=1;i <= k;++i)
 65             for(int j=0;j <= (total-(1<<i));++j)
 66                 if(depth[dp[i-1][j]] > depth[dp[i-1][j+(1<<(i-1))]])//dp[i][j] : 记录的是下标
 67                     dp[i][j]=dp[i-1][j+(1<<(i-1))];
 68                 else
 69                     dp[i][j]=dp[i-1][j];
 70     }
 71     int Lca(int u,int v)
 72     {
 73         if(u > v)
 74             swap(u,v);
 75         int k=log(v-u+1)/log(2);
 76         if(depth[dp[k][u]] > depth[dp[k][v-(1<<k)+1]])
 77             return vs[dp[k][v-(1<<k)+1]];
 78         return vs[dp[k][u]];//返回 u,v 的lca
 79     }
 80 }_rmq;
 81 //=====================================
 82 void Dfs(int u,int f,int d)
 83 {
 84     vs[total]=u;
 85     depth[total]=d;
 86     id[u]=total++;
 87     for(int i=0;i < G[u].size();++i)
 88     {
 89         Node &e=G[u][i];
 90         if(e.to != f)
 91         {
 92             Add(total,e.w);//叶子方向,+e.w
 93             es[2*e.id]=total;//记录朝向叶子方向的边
 94             Dfs(e.to,u,d+1);
 95             vs[total]=u;
 96             depth[total++]=d;
 97             Add(total,-e.w);//根方向, -e.w
 98             es[2*e.id+1]=total;//记录朝向根方向的边
 99         }
100     }
101 }
102 void Init()
103 {
104     _rmq.Init();
105     total=0;
106     mem(bit,0);
107     for(int i=0;i < maxn;++i)
108         G[i].clear();
109 }
110 int main()
111 {
112     while(~scanf("%d%d%d",&n,&q,&s))
113     {
114         Init();
115         for(int i=1;i < n;++i)
116         {
117             int u,v;
118             scanf("%d%d%d",&u,&v,w+i);
119             addEdge(u,v,w[i],i);
120         }
121         Dfs(1,-1,0);
122         _rmq.ST();
123         for(int i=1;i <= q;++i)
124         {
125             int type;
126             scanf("%d",&type);
127             if(type == 0)
128             {
129                 int u;
130                 scanf("%d",&u);
131                 int lca=_rmq.Lca(id[u],id[s]);
132                 printf("%d\n",Sum(id[u])+Sum(id[s])-2*Sum(id[lca]));
133                 s=u;
134             }
135             else
136             {
137                 int x,val;
138                 scanf("%d%d",&x,&val);
139                 Add(es[x*2],val-w[x]);//w[x] 变为 val,需要在原基础上加上 val-w[x]
140                 Add(es[x*2+1],w[x]-val);//朝向根方向的加负值
141                 w[x]=val;
142             }
143         }
144     }
145     return 0;
146 }
RMQ+BIT

  

 

转载于:https://www.cnblogs.com/violet-acmer/p/9760319.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值