【树链剖分】FZU 2082 过路费 求和

点击打开链接

因为在树上任意两点间只有一条路径,所以只有一条路径。。。

所以求出两点间的费用即可

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int maxn=101010+5;
const int maxm=maxn+maxn;
#define ll __int64
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
struct EDGENODE{
    int to;
    int w;
    int next;
}edges[maxm];
int head[maxn],edge;
inline void init(){
    edge=0;
    memset(head,-1,sizeof(head));
}
inline void addedge(int u,int v,int w){
    edges[edge].w=w,edges[edge].to=v,edges[edge].next=head[u],head[u]=edge++;
    edges[edge].w=w,edges[edge].to=u,edges[edge].next=head[v],head[v]=edge++;
}
int que[maxn]; // 队列
bool vis[maxn]; // 访问标记
int son[maxn]; // 重儿子
int idx[maxn]; // 结点v在其路径中的编号
int dep[maxn]; // 结点v的深度
int siz[maxn]; // 以结点v为根的子树的结点个数
int belong[maxn]; // 结点v所属的路径编号
int fa[maxn]; // 结点v的父亲结点
int top[maxn]; // 编号为p的路径的顶端结点
int len[maxn]; // 路径p的长度
int sump[maxn]; // 路径p的编号
int seg[maxn]; // 结点v的父边在线段树中的位置
int wei[maxn]; // 结点v的父边的权值
int l,r,ans,cnt;
int n;
char cmd[22];
int num[maxn];
void split(){
    memset(dep,-1,sizeof(dep));
    l=0;
    dep[ que[r=1]=1 ]=0; // 将根结点插入队列,并设深度为0
    fa[1]=-1; // 默认 1 为根结点
    wei[1]=0;
    while (l<r){ // 第一遍搜索求出 fa,dep,wei
        int u=que[++l];
        vis[u]=false; // 顺便初始化vis
        for (int i=head[u];i!=-1;i=edges[i].next){
            int v=edges[i].to;
            int w=edges[i].w;
            if (dep[v]==-1){ // 未访问过的结点
                dep[ que[++r]=v ]=dep[u]+1; // 将v插入队列并设深度为dep[u]+1
                fa[v]=u; // v的父结点为u
                wei[v]=w; // v的父边权值
            }
        }
    }
    cnt=0; // 重链编号
    for (int i=n;i>0;i--){
        int u=que[i],p=-1;
        siz[u]=1;
        son[u]=p;
        for (int k=head[u];k!=-1;k=edges[k].next){
            int v=edges[k].to;
            if (vis[v]){ // 若v是u的子结点
                siz[u]+=siz[v]; // 计数
                if (p==-1||siz[v]>siz[p]){
                    son[u]=v;
                    p=v; // u的重儿子是v
                }
            }
        }
        if (p==-1){ // u是叶子结点
            idx[u]=len[++cnt]=1; // 一个新的路径编号为cnt,u是路径中的第一个结点
            belong[ top[cnt]=u ]=cnt; // u是顶端结点,且u属于路径cnt
        }
        else{ // u不是叶子结点
            idx[u]=++len[ belong[u]=belong[p] ]; // u属于重儿子所在的链,链长+1,u是路径中第len个结点
            top[ belong[u] ]=u; // u是顶端结点
        }
        vis[u]=true; // 访问标记
    }
}
struct tree
{
    ll sum[maxn<<2];
    bool lazy[maxn<<2];
    void pushup(int rt)
    {
        sum[rt]=sum[rt<<1]+sum[rt<<1|1];
    }
    void build(int l,int r,int rt)
    {
        if(l==r)
        {
            sum[rt]=num[l];
            return ;
        }
        int m=(l+r)>>1;
        build(lson);
        build(rson);
        pushup(rt);
    }
    void update(int add,int flag,int l,int r,int rt)
    {
        if(l==r)
        {
            sum[rt]=add;
            return ;
        }
        int m=(l+r)>>1;
        if(flag<=m)
            update(add,flag,lson);
        else update(add,flag,rson);
        pushup(rt);
    }
    ll query(int L,int R,int l,int r,int rt)
    {
        if(L<=l&&r<=R)
        {
            return sum[rt];
        }
        int m=(l+r)>>1,ans=0;
        if(L<=m)
            ans=ans+query(L,R,lson);
        if(R>m)
            ans=ans+query(L,R,rson);
        return ans;
    }
}xixixi;
ll find(int va,int vb){
    int f1=top[belong[va]],f2=top[belong[vb]];
    ll tmp=0;
    while (f1!=f2){
        if (dep[f1]<dep[f2]){
            swap(f1,f2);
            swap(va,vb);
        }
        tmp=tmp+xixixi.query(seg[f1],seg[va],1,n,1);
        va=fa[f1];
        f1=top[belong[va]];
    }
    if (va==vb) return tmp;
    if (dep[va]>dep[vb]) swap(va,vb);
    return tmp+xixixi.query(seg[son[va]],seg[vb],1,n,1);
}
int d[maxn][3];

int main()
{
    int m;
    while (cin>>n>>m){
        init();
        for (int i=1;i<n;i++){
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            d[i][0]=a;
            d[i][1]=b;
            d[i][2]=c;
            addedge(a,b,c);
        }
        split();
        sump[0]=0;
        for (int i=1;i<=cnt;i++) sump[i]=sump[i-1]+len[i];
        for (int i=1;i<=n;i++){
            seg[i]=sump[ belong[i] ]-idx[i]+1;
            num[ seg[i] ]=wei[i];
        }
        xixixi.build(1,n,1);
        int x,y,z;
        while (m--){
        scanf("%d%d%d",&x,&y,&z);
            if (x==1){
                printf("%I64d\n",find(y,z));
            }
            if (x==0){
                if (fa[d[y][1]]==d[y][0]) xixixi.update(z,seg[d[y][1]],1,n,1);
                else xixixi.update(z,seg[d[y][0]],1,n,1);
            }
        }
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值