Housewife Wind 【LCA转RMQ 求最短路+边权修改】or 【树链剖分】

来源:互联网 发布:国家发改委 大数据 编辑:程序博客网 时间:2024/05/17 21:49

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
题意
题意:有N个点和N-1条无向边构成了一棵树。给你起点S和Q次查询,查询分两种:
一,0 a 让你求所在节点到a的最短距离;二,1 a b 把第 a 条边的边权改为b。

操作一没问题,主要是 真对操作二,反正不可能修改一次,全部重新RMQ一次,===》注意到 题目中关键的一个条件,在这个图中,任意的两个点之间只有单一路径并且图是连通的,所以 题目中所给的 图是 无环的无向图。 所以可以构成一颗树 ;
求操作一时候,用的是dist【】;
所以 如果一个边修改后,边的两点,肯定一个点u是另一个点v的父节点, 所以 ===》因为是无环的,所以肯定不会出现 以v根节点的子树上的子节点 指向别的子树,所以我们只是更新以v为根节点的 子树上的dist值就够了 ;

注意这里询问次数多,在线算法 (步近法肯定不行,当然还有别的高效的在线算法 应该也是可以的(我还没有学))。所以这里用的是离线算法 LCA转RMQ
看代码把

#include <cstdio>#include <cstring>#include <queue>#include <vector>#include <algorithm>#define MAXN 100001+100#define MAXM 400000+100using 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 pre[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;    pre[u]=fa;    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));    memset(pre,-1,sizeof(pre));    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)];}queue<int>q;  // 这里可以用dfs or bfs ,但是可能修改的次数多,用了队列stl的bfs时间上可能更费时间些。bfs 3000ms dfs 2500msvoid work(int now,int change){    while(!q.empty()) q.pop();     q.push(now);     while(!q.empty())     {        int now=q.front();q.pop();        dist[now]+=change;  //修改        for(int i=head[now];i!=-1;i=edge[i].next)        {        int nexts=edge[i].to;        if(nexts==pre[now]) continue;        q.push(nexts);     }    }}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;//改变量            int a = edge[k].from;             int b = edge[k].to;//记录变动边的起点终点            //因为该边权值被改变,深度大于它们的点的dist也要改变            int v = node[a] > node[b] ? a : b;            work(v,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 secondusing 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;}
原创粉丝点击