poj 2763 Housewife Wind 【LCA or 树链剖分】

Housewife Wind
Time Limit: 4000MS   Memory Limit: 65536K
Total Submissions: 7444   Accepted: 1925

Description

After their royal wedding, Jiajia and Wind hid away in XX Village, to enjoy their ordinary happy life. People in XX Village lived in beautiful huts. There are some pairs of huts connected by bidirectional roads. We say that huts in the same pair directly connected. XX Village is so special that we can reach any other huts starting from an arbitrary hut. If each road cannot be walked along twice, then the route between every pair is unique. 

Since Jiajia earned enough money, Wind became a housewife. Their children loved to go to other kids, then make a simple call to Wind: 'Mummy, take me home!' 

At different times, the time needed to walk along a road may be different. For example, Wind takes 5 minutes on a road normally, but may take 10 minutes if there is a lovely little dog to play with, or take 3 minutes if there is some unknown strange smell surrounding the road. 

Wind loves her children, so she would like to tell her children the exact time she will spend on the roads. Can you help her? 

Input

The first line contains three integers n, q, s. There are n huts in XX Village, q messages to process, and Wind is currently in hut s. n < 100001 , q < 100001. 

The following n-1 lines each contains three integers a, b and w. That means there is a road directly connecting hut a and b, time required is w. 1<=w<= 10000. 

The following q lines each is one of the following two types: 

Message A: 0 u 
A kid in hut u calls Wind. She should go to hut u from her current position. 
Message B: 1 i w 
The time required for i-th road is changed to w. Note that the time change will not happen when Wind is on her way. The changed can only happen when Wind is staying somewhere, waiting to take the next kid. 

Output

For each message A, print an integer X, the time required to take the next child.

Sample Input

3 3 1
1 2 1
2 3 2
0 2
1 2 3
0 3

Sample Output

1

3

WA8次才过,就因为用错了数组。。。

题意:有N个点和N-1条无向边构成了一棵树。给你起点S和Q次查询,查询分两种:

一,0 a 让你求所在节点到a的最短距离;二,1 a b 把第 a 条边的边权改为b。

思路:用dist[i]存储节点i到root的最短距离,用node[i]记录i节点在DFS树中的深度。

1,求两节点间最短距离——LCA转RMQ算法解决,对于a、b两点距离,有dist[a] + dist[b] - 2 * dist[LCA(a, b)];

2,修改边权的环节——邻接表建边,找到要修改的边很简单。但一条边边权的修改最坏情况下会影响到N-1个点的dist[]值,这样对下面查询两点最短距离造成很大影响,我们要做的是——更新那些受影响的点的dist值。设要修改的边起点为a,终点为b。那我们必须要知道这条边边权的改变影响到哪些点的dist值,显然,树中被影响的节点是——深度大于max(node[a], node[b])的点(并不是所有),知道这点实现更新dist[]就简单了。

过程:

记录修改边起点a和终点b并求出边权变化量change(变化后的 - 变化前的)。然后从max(node[a], node[b])j即深度较大的点向下搜索,在搜索过程中修改dist值 即 dist[] += change。

注意:在我的代码work(u, fa, change)中,u 是起点和终点里深度较大的点,fa则看作是u的父节点(深度比u小),不要弄混淆了。还有起点S要随时更新。

AC代码:

#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
#include <algorithm>
#define MAXN 100000+100
#define MAXM 400000+100
using namespace std;
struct Edge
{
    int from, to, val, next;
};
Edge edge[MAXM];
int head[MAXN], edgenum;
int vs[MAXN<<1];
int depth[MAXN<<1];
int node[MAXN];//记录节点的深度
int id[MAXN];
int dfs_clock;//时间戳
int dist[MAXN];
int N, Q, S;//N个点 Q次查询 S起点
void init()
{
    edgenum = 0;
    memset(head, -1, sizeof(head));
}
void addEdge(int u, int v, int w)
{
    Edge E = {u, v, w, head[u]};
    edge[edgenum] = E;
    head[u] = edgenum++;
}
void getMap()
{
    int a, b, c;
    for(int i = 1; i < N; i++)
    {
        scanf("%d%d%d", &a, &b, &c);
        addEdge(a, b, c), addEdge(b, a, c);
    }
}
void DFS(int u, int fa, int d)//遍历整棵树
{
    node[u] = d;//u节点的深度优先数
    id[u] = dfs_clock;
    vs[dfs_clock] = u;
    depth[dfs_clock++] = d;
    for(int i = head[u]; i != -1; i = edge[i].next)
    {
        int v = edge[i].to;
        if(v == fa) continue;
        dist[v] = dist[u] + edge[i].val;
        DFS(v, u, d+1);
        vs[dfs_clock] = u;
        depth[dfs_clock++] = d;
    }
}
void find_depth()
{
    dfs_clock = 1;
    memset(vs, 0, sizeof(vs));
    memset(depth, 0, sizeof(depth));
    memset(node, 0, sizeof(node));
    memset(id, 0, sizeof(id));
    memset(dist, 0, sizeof(dist));
    DFS(1, -1, 0);
}
int dp[MAXN<<1][30];
void RMQ_init(int NN)//RMQ预处理区间 最小值
{
    for(int i = 1; i <= NN; i++)
        dp[i][0] = i;
    for(int j = 1; (1<<j) <= NN; j++)
    {
        for(int i = 1; i + (1<<j) - 1 <= NN; i++)
        {
            int a = dp[i][j-1];
            int b = dp[i + (1<<(j-1))][j-1];
            if(depth[a] <= depth[b])
                dp[i][j] = a;
            else
                dp[i][j] = b;
        }
    }
}
int query(int L, int R)
{
    int k = 0;
    while((1<<(k+1)) <= R-L+1) k++;
    int a = dp[L][k];
    int b = dp[R - (1<<k) + 1][k];
    if(depth[a] <= depth[b])
        return a;
    else
        return b;
}
int LCA(int u, int v)
{
    int x = id[u];
    int y = id[v];
    if(x >= y)
        return vs[query(y, x)];
    else
        return vs[query(x, y)];
}
void work(int u, int fa, int change)
{
    dist[u] += change;
    for(int i = head[u]; i != -1; i = edge[i].next)
    {
        int v = edge[i].to;
        if(v == fa) continue;
        work(v, u, change);
    }
}
void solve()
{
    int op, a, b;
    while(Q--)
    {
        scanf("%d", &op);
        if(op == 0)
        {
            scanf("%d", &a);
            printf("%d\n", dist[S] + dist[a] - 2 * (dist[LCA(S, a)]));
            S = a;//变换起点
        }
        else
        {
            scanf("%d%d", &a, &b);//把第a条边上的权值改为b
            int k = (a-1)<<1;//边的编号
            int change = b - edge[k].val;//改变量
            edge[k].val = edge[k^1].val = b;//改变
            int a = edge[k].from; int b = edge[k].to;//记录变动边的起点终点
            //因为该边权值被改变,深度大于它们的点的dist也要改变
            int u = node[a] < node[b] ? a : b;
            int v = node[a] > node[b] ? a : b;
            work(v, u, change);//改变 所有被影响的dist值
        }
    }
}
int main()
{
    while(scanf("%d%d%d", &N, &Q, &S) != EOF)
    {
        init();
        getMap();
        find_depth();
        RMQ_init(dfs_clock - 1);
        solve();
    }
    return 0;
}




树链剖分:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <vector>
#include <string>
#define INF 0x3f3f3f3f
#define eps 1e-8
#define MAXN (100000+10)
#define MAXM (300000+10)
#define Ri(a) scanf("%d", &a)
#define Rl(a) scanf("%lld", &a)
#define Rf(a) scanf("%lf", &a)
#define Rs(a) scanf("%s", a)
#define Pi(a) printf("%d\n", (a))
#define Pf(a) printf("%.2lf\n", (a))
#define Pl(a) printf("%lld\n", (a))
#define Ps(a) printf("%s\n", (a))
#define W(a) while((a)--)
#define CLR(a, b) memset(a, (b), sizeof(a))
#define MOD 1000000007
#define LL long long
#define lson o<<1, l, mid
#define rson o<<1|1, mid+1, r
#define ll o<<1
#define rr o<<1|1
#define PI acos(-1.0)
#pragma comment(linker, "/STACK:102400000,102400000")
#define fi first
#define se second
using namespace std;
struct Tree{
    int l, r, sum;
};
Tree tree[MAXN<<2];
void PushUp(int o){
    tree[o].sum = tree[ll].sum + tree[rr].sum;
}
void Build(int o, int l, int r)
{
    tree[o].l = l; tree[o].r = r;
    tree[o].sum = 0;
    if(l == r)
        return ;
    int mid = (l + r) >> 1;
    Build(lson); Build(rson);
}
void Update(int o, int pos, int v)
{
    if(tree[o].l == tree[o].r)
    {
        tree[o].sum = v;
        return ;
    }
    int mid = (tree[o].l + tree[o].r) >> 1;
    if(pos <= mid) Update(ll, pos, v);
    else Update(rr, pos, v);
    PushUp(o);
}
int Query(int o, int L, int R)
{
    if(tree[o].l == L && tree[o].r == R)
        return tree[o].sum;
    int mid = (tree[o].l + tree[o].r) >> 1;
    if(R <= mid) return Query(ll, L, R);
    else if(L > mid) return Query(rr, L, R);
    else return Query(ll, L, mid) + Query(rr, mid+1, R);
}
struct Edge{
    int from, to, val, next;
};
Edge edge[MAXN<<1];
int head[MAXN], edgenum;
void init(){
    edgenum = 0; CLR(head, -1);
}
void addEdge(int u, int v, int w)
{
    Edge E = {u, v, w, head[u]};
    edge[edgenum] = E;
    head[u] = edgenum++;
}
int son[MAXN], num[MAXN];
int top[MAXN], pos[MAXN], id;
int dep[MAXN], pre[MAXN];
void DFS1(int u, int fa, int d)
{
    dep[u] = d; pre[u] = fa; num[u] = 1; son[u] = -1;
    for(int i = head[u]; i != -1; i = edge[i].next)
    {
        int v = edge[i].to;
        if(v == fa) continue;
        DFS1(v, u, d+1);
        num[u] += num[v];
        if(son[u] == -1 || num[son[u]] < num[v])
            son[u] = v;
    }
}
void DFS2(int u, int T)
{
    top[u] = T; pos[u] = ++id;
    if(son[u] == -1) return ;
    DFS2(son[u], T);
    for(int i = head[u]; i != -1; i = edge[i].next)
    {
        int v = edge[i].to;
        if(v == pre[u] || v == son[u]) continue;
        DFS2(v, v);
    }
}
int Dist(int u, int v)
{
    int f1 = top[u], f2 = top[v];
    int ans = 0;
    while(f1 != f2)
    {
        if(dep[f1] < dep[f2])
        {
            swap(u, v);
            swap(f1, f2);
        }
        ans += Query(1, pos[f1], pos[u]);
        u = pre[f1], f1 = top[u];
    }
    if(u == v) return ans;
    if(dep[u] > dep[v]) swap(u, v);
    ans += Query(1, pos[son[u]], pos[v]);
    return ans;
}
int s[MAXN], e[MAXN], c[MAXN];
int main()
{
    int n, S, q;
    while(scanf("%d%d%d", &n, &q, &S) != EOF)
    {
        init();
        for(int i = 1; i <= n-1; i++)
        {
            Ri(s[i]), Ri(e[i]), Ri(c[i]);
            addEdge(s[i], e[i], c[i]);
            addEdge(e[i], s[i], c[i]);
        }
        DFS1(1, -1, 1); id = 0; DFS2(1, 1); Build(1, 1, id);
        for(int i = 1; i <= n-1; i++)
        {
            if(dep[s[i]] > dep[e[i]])
                swap(s[i], e[i]);
            Update(1, pos[e[i]], c[i]);
        }
        W(q)
        {
            int op; Ri(op); int x, y;
            if(op == 1)
            {
                Ri(x); Ri(y);
                Update(1, pos[e[x]], y);
            }
            else
            {
                Ri(x);
                Pi(Dist(S, x));
                S = x;
            }
        }
    }
    return 0;
}


你可能感兴趣的:(poj 2763 Housewife Wind 【LCA or 树链剖分】)