poj2763 Housewife Wind 在线LCA+BIT

      给一棵N个节点的边上带权树,给一个起点,接下来有M个操作,0 i:从当前位置走到i点,并且输出路径和;1 i w:把第i条边的权值变成w。整体上思路还是dis[x]+dis[y]-2*dis[lca(x,y)],但是更新时如果一条一条的去更新,显然会超时..这里可以结合dfs序和树状数组来实现更新和查询。首先dfs的时候给各个节点重新编号打上时间戳,这样每个节点的子树都会是一个连续的区间,那么某条边被改变的时候,先找到这条边连接的深度较大的点x,x子树的其实加上c与当前边权的差值,x子树的结束点减掉这部分值,就实现了这个区间的更新。查询的时候求一下LCA(x,y),在bit里找出对应的三个值就行。注意更新边的时候不仅要修改bit里的值,还要把这条边的值修改...这里漏了一句话结果调了一下午.....

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;
typedef int ll;
const int maxn=250000;
const int POW=19;
int n,m;
struct BIT
{
    ll dt[maxn];
    void init()
    {
        memset(dt,0,sizeof dt);
    }
    int lowbit(int x)
    {
        return x&(-x);
    }
    void modify(int x,ll c)
    {
        if (x==0 || x>n) return;
        for (int x1=x; x1<=n; x1+=lowbit(x1))
        {
            dt[x1]+=c;
        }
    }
    ll query(int x)
    {
        ll res=0;
        for (int x1=x; x1>0; x1-=lowbit(x1))
        {
            res+=dt[x1];
        }
        return res;
    }
}bit;
struct EDGE
{
    int f,v,w,next,id;
}edge[maxn<<2];
int g[maxn];
int d[maxn];
int p[maxn][20];
int dfsclock;
int l[maxn],r[maxn];
void dfs(int u,int fa)
{
    l[u]=++dfsclock;
    d[u]=d[fa]+1;
    p[u][0]=fa;
    for (int i=1; i<POW; i++) p[u][i]=p[p[u][i-1]][i-1];
    for (int j=g[u]; j!=-1; j=edge[j].next)
    {
        int v=edge[j].v;
        if (v==fa) continue;
        dfs(v,u);
        r[v]=dfsclock+1;
//        dis[id[v]]=dis[id[u]]+edge[j].w;
        bit.modify(l[v],edge[j].w);
        bit.modify(r[v],-edge[j].w);
    }
}
int lca(int a,int b)
{
    if (d[a]>d[b]) swap(a,b);
    if (d[a]<d[b])
    {
        int del=d[b]-d[a];
        for (int i=0; i<POW; i++) if (del&(1<<i)) b=p[b][i];
    }
    if (a!=b)
    {
        for (int i=POW-1; i>=0; i--)
        {
            if (p[a][i]!=p[b][i])
            a=p[a][i],b=p[b][i];
        }
        a=p[a][0];
        b=p[b][0];
    }
    return a;
}
int s,k,x,y,z;
int cn;
int fr[maxn],to[maxn],w[maxn];
int main()
{
//    freopen("in.txt","r",stdin);
    while(~scanf("%d%d%d",&n,&m,&s))
    {
        bit.init();
        memset(g,-1,sizeof g);
        memset(p,0,sizeof p);
        memset(d,0,sizeof d);
        memset(l,0,sizeof l);
        memset(r,0,sizeof r);
        memset(fr,0,sizeof fr);
        memset(to,0,sizeof to);
        memset(w,0,sizeof w);


        cn=0;
        dfsclock=0;
        for (int i=1; i<n; i++)
        {
            scanf("%d%d%d",&fr[i],&to[i],&z);
            x=fr[i];
            y=to[i];
            w[i]=z;
            edge[cn].v=y;
            edge[cn].w=z;
            edge[cn].next=g[x];
            g[x]=cn;
            cn++;

            edge[cn].v=x;
            edge[cn].w=z;
            edge[cn].next=g[y];
            g[y]=cn;
            cn++;
        }
        dfs(1,-1);
        for (int i=1; i<=m; i++)
        {
            scanf("%d",&x);
            if (x==0)
            {
                scanf("%d",&y);
                z=lca(s,y);
                ll aa,bb,cc;
                aa=bit.query(l[s]);
                bb=bit.query(l[y]);
                cc=(bit.query(l[z])<<1);
                cout<<aa+bb-cc<<endl;
                s=y;
            }
            else
            {
                scanf("%d%d",&y,&z);
                int t;
                if (d[fr[y]]>d[to[y]]) t=fr[y];
                else t=to[y];
                bit.modify(l[t],z-w[y]);
                bit.modify(r[t],w[y]-z);
                w[y]=z;
            }
        }
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值