LCA+树状数组 POJ 2763 Housewife Wind

 

题目传送门

题意:两种操作,问u到v的距离,并且u走到了v;把第i条边距离改成w

分析:根据DFS访问顺序,将树处理成链状的,那么回边处理成负权值,那么LCA加上BIT能够知道u到v的距离,BIT存储每条边的信息,这样第二种操作也能用BIT快速解决

利用RMQ的写法不知哪里写挫了,改用倍增法

 

/************************************************
* Author        :Running_Time
* Created Time  :2015/10/6 星期二 11:45:03
* File Name     :POJ_2763.cpp
 ************************************************/

#include <cstdio>
#include <algorithm>
#include <iostream>
#include <sstream>
#include <cstring>
#include <cmath>
#include <string>
#include <vector>
#include <queue>
#include <deque>
#include <stack>
#include <list>
#include <map>
#include <set>
#include <bitset>
#include <cstdlib>
#include <ctime>
using namespace std;
#define lson l, mid, rt << 1
#define rson mid + 1, r, rt << 1 | 1
typedef long long ll;
const int N = 1e5 + 10;
const int D = 20;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
const double EPS = 1e-8;
struct Edge {
    int v, w, id, nex;
}edge[N<<1];
struct BIT  {
    int c[N<<1], NN;
    void init(int n) {
        NN = n * 2;
        memset (c, 0, sizeof (c));
    }
    void updata(int i, int x)   {
        while (i <= NN) {
            c[i] += x;  i += i & (-i);
        }
    }
    int query(int i)    {
        int ret = 0;
        while (i)   {
            ret += c[i];    i -= i & (-i);
        }
        return ret;
    }
}bit;
int head[N];
int dep[N], rt[D][N], id[N], in[N], out[N];
int cost[N];
int e, tim;

void init(void) {
    memset (head, -1, sizeof (head));
    e = 0;
}

void add_edge(int u, int v, int w, int id)  {
    edge[e] = (Edge) {v, w, id, head[u]};
    head[u] = e++;
}

void DFS(int u, int fa, int d) {
    dep[u] = d; rt[0][u] = fa;
    for (int i=head[u]; ~i; i=edge[i].nex)  {
        Edge &e = edge[i];
        if (e.v == fa)  continue;
        in[e.id] = id[e.v] = ++tim;
        DFS (e.v, u, d + 1);
        out[e.id] = ++tim;
    }
}

int LCA(int u, int v)   {
    if (dep[u] < dep[v])    {
        swap (u, v);
    }
    for (int i=0; i<D; ++i) {
        if ((dep[u] - dep[v]) >> i & 1) {
            u = rt[i][u];
        }
    }
    if (u == v) return u;
    for (int i=D-1; i>=0; --i)  {
        if (rt[i][u] != rt[i][v])   {
            u = rt[i][u];
            v = rt[i][v];
        }
    }
    return rt[0][u];
}

int main(void)    {
    int n, q, s;
    while (scanf ("%d%d%d", &n, &q, &s) == 3)   {
        init ();
        for (int u, v, w, i=1; i<n; ++i)    {
            scanf ("%d%d%d", &u, &v, &cost[i]);
            add_edge (u, v, cost[i], i);
            add_edge (v, u, cost[i], i);
        }
        tim = 0;
        DFS (1, -1, 0);
        for (int i=1; i<D; ++i) {
            for (int j=1; j<=n; ++j)    {
                rt[i][j] = rt[i-1][j] == -1 ? -1 : rt[i-1][rt[i-1][j]];
            }
        }

        bit.init (n);
        for (int i=1; i<n; ++i) {
            bit.updata (in[i], cost[i]);        //入边序号
            bit.updata (out[i], -cost[i]);      //回边序号
        }

        int u = s;
        for (int op, i=1; i<=q; ++i)    {
            scanf ("%d", &op);
            if (op == 0)    {
                int v;  scanf ("%d", &v);               //入点序号和回点序号
                printf ("%d\n", bit.query (id[u]) + bit.query (id[v]) - bit.query (id[LCA (u, v)]) * 2);
                u = v;
            }
            else    {
                int p, w;   scanf ("%d%d", &p, &w);
                bit.updata (in[p], w - cost[p]);
                bit.updata (out[p], cost[p] - w);
                cost[p] = w;
            }
        }
    }   

    return 0;
}

  

转载于:https://www.cnblogs.com/Running-Time/p/4857340.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
LCA+路径压缩的方式可以用于求解树上的桥,具体实现步骤如下: 1. 对于树上每个节点,记录其在树中的深度(或者高度)以及其父亲节点。 2. 对于每个节点,记录其在树上的最小深度(或最小高度)以及其所在子树中深度最小的节点。 3. 对于每条边(u, v),设u的深度小于v的深度(或者高度),则如果v的子树中没有深度小于u的节点,则(u, v)是桥。 具体的实现过程如下: 首先,我们需要对树进行预处理,求出每个节点的深度以及其父亲节点。可以使用深度优先搜索(DFS)或广度优先搜索(BFS)来实现。在这里我们使用DFS来实现: ```c++ vector<int> adj[MAX_N]; // 树的邻接表 int n; // 树的节点数 int dep[MAX_N], fa[MAX_N]; // dep[i]表示节点i的深度,fa[i]表示节点i的父亲节点 void dfs(int u, int f, int d) { dep[u] = d; fa[u] = f; for (int v : adj[u]) { if (v != f) { dfs(v, u, d + 1); } } } ``` 接下来,我们需要计算每个节点所在子树中深度最小的节点。我们可以使用LCA(最近公共祖先)的方法来实现。具体来说,我们可以使用倍增算法来预处理出每个节点的2^k级祖先,并且在查询LCA时使用路径压缩的方式优化时间复杂度。这里我们不展开讲解LCA和倍增算法的细节,如果你对此感兴趣,可以参考其他资料进行学习。 ```c++ const int MAX_LOG_N = 20; // log2(n)的上取整 int anc[MAX_N][MAX_LOG_N]; // anc[i][j]表示节点i的2^j级祖先 int mn[MAX_N]; // mn[i]表示节点i所在子树中深度最小的节点 void precompute() { // 预处理anc数组 for (int j = 1; j < MAX_LOG_N; j++) { for (int i = 1; i <= n; i++) { if (anc[i][j - 1] != -1) { anc[i][j] = anc[anc[i][j - 1]][j - 1]; } } } // 计算mn数组 for (int i = 1; i <= n; i++) { mn[i] = i; for (int j = 0; (1 << j) <= dep[i]; j++) { if ((dep[i] & (1 << j)) != 0) { mn[i] = min(mn[i], mn[anc[i][j]]); i = anc[i][j]; } } } } ``` 最后,我们可以使用LCA+路径压缩的方式来判断每条边是否为桥。具体来说,对于每条边(u, v),我们需要判断v的子树中是否存在深度小于u的节点。如果存在,则(u, v)不是桥,否则(u, v)是桥。 ```c++ bool is_bridge(int u, int v) { if (dep[u] > dep[v]) swap(u, v); if (mn[v] != u) return true; // 子树中存在深度小于u的节点 return false; // 子树中不存在深度小于u的节点 } ``` 完整代码如下:

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值