題解 三隻企鵝 樹鏈剖分

題解 三隻企鵝

題目描述

在這裏插入圖片描述
數據範圍:n,m,w105,u,vnn,m,w \leq 10^5,u,v \leq n

具體做法與心路歷程

考場上的錯誤方法不多說。

具體做法

我們把查詢操作寫出來:
ans=vModifydisu+disv2dislcau,v ans=\sum_{v \in Modify}{dis_u+dis_v-2dis_{lca_{u,v}}}
我們發現前面兩個disdis比較好求。重點是怎麼求dislcau,vdis_{lca_{u,v}}

因爲直接修改其他所有點的距離沒有優秀的方法,所以我們試着把修改改成差分

對於u,vu,v,如果他們在rootroot的不同子樹中,那麼他們的dislcau,v=0dis_{lca_{u,v}}=0,不用考慮。

也就是說,對於一個修改操作vv,他能影響到的點只在他所在的到根的子樹中。

我們把vv到根的路徑上的每個點都打一個標記,記uu的標記個數爲cntucnt_u,考慮查詢時求出uu的每個祖先作爲lcalca的個數再來計算。

那麼有如下計算方式:

  1. disfu×(cntfucntu)dis_{f_u} \times (cnt_{f_u}-cnt_u)
  2. u=fuu=f_u
  3. 重複1,2,直到uu爲根

將以上拆開後把cntucnt_u放在一起有:cntu×(disudisfu)cnt_u \times (dis_u - dis_{f_u})。那麼這個查詢就變成查詢uu到根上這些所有的和了。

我們可以輕易維護好cntucnt_u(樹鏈剖分),那麼我們用線段樹維護每個點uudisudisfudis_{u}-dis_{f_u}的倍數,最後區間求和即可。(意思就是區間加時對於每個點加的值不一樣)

如果不清楚可以看代碼。

Code\mathcal{Code}

/*******************************
Author:galaxy yr
LANG:C++
Created Time:2019年10月28日 星期一 20時55分56秒
*******************************/
#include<cstdio>
#include<algorithm>
#define int long long

using namespace std;

struct IO{
    template<typename T>
    IO & operator>>(T&res)
    {
        T q=1;char ch;
        while((ch=getchar())<'0' or ch>'9')if(ch=='-')q=-q;
        res=(ch^48);
        while((ch=getchar())>='0' and ch<='9') res=(res<<1)+(res<<3)+(ch^48);
        res*=q;
        return *this;
    }
}cin;

struct edge{
    int to,next,w;
    edge(int a=0,int b=0,int c=0):to(a),next(b),w(c){}
};

const int maxn=2e5+10;
int n,m,head[maxn],cnt,f[maxn],top[maxn],size[maxn],seg[maxn],son[maxn],dis[maxn],tot,rev[maxn];
long long res;
edge e[maxn<<1];

void add(int u,int v,int w)
{
    e[++cnt]=edge(v,head[u],w);
    head[u]=cnt;
}

void dfs1(int now,int fa)
{
    f[now]=fa; size[now]=1;
    for(int i=head[now];i;i=e[i].next)
        if(e[i].to!=fa)
        {
            dis[e[i].to]=dis[now]+e[i].w;
            dfs1(e[i].to,now);
            size[now]+=size[e[i].to];
            if(size[son[now]]<size[e[i].to])
                son[now]=e[i].to;
        }
}

void dfs2(int now,int tp)
{
    seg[now]=++seg[0];
    rev[seg[0]]=now;
    top[now]=tp;
    if(son[now])
        dfs2(son[now],tp);
    for(int i=head[now];i;i=e[i].next)
        if(e[i].to!=f[now] && e[i].to!=son[now])
            dfs2(e[i].to,e[i].to);
}

/*{{{線段樹*/

namespace SegmentTree{

    long long sum[maxn*4],lazy[maxn*4],delta[maxn*4];


    void update(int k)
    {
        sum[k]=sum[k<<1]+sum[k<<1|1];
    }

    void work(int k,long long val)
    {
        sum[k]+=delta[k]*val;
        lazy[k]+=val;
    }

    void pushdown(int k)
    {
        if(!lazy[k]) return;
        work(k<<1,lazy[k]);
        work(k<<1|1,lazy[k]);
        lazy[k]=0;
    }

    void build(int k,int l,int r)
    {
        if(l==r)
        {
            delta[k]=dis[rev[l]]-dis[f[rev[l]]];
            return;
        }
        int mid=(l+r)>>1;
        build(k<<1,l,mid);
        build(k<<1|1,mid+1,r);
        delta[k]=delta[k<<1]+delta[k<<1|1];
    }

    void modify(int k,int l,int r,int x,int y,int val)
    {
        if(l>=x && r<=y) return work(k,val);
        if(l>y  ||  r<x) return;
        int mid=(l+r)>>1;
        pushdown(k);
        modify(k<<1,l,mid,x,y,val); modify(k<<1|1,mid+1,r,x,y,val);
        return update(k);
    }

    long long query(int k,int l,int r,int x,int y)
    {
        if(l>=x && r<=y) return sum[k];
        if(l>y  ||  r<x) return 0;
        int mid=(l+r)>>1;
        pushdown(k);
        return query(k<<1,l,mid,x,y)+query(k<<1|1,mid+1,r,x,y);
    }

};

/*}}}*/

void modify(int now)
{
    ++tot; res+=dis[now];
    while(top[now])
    {
        SegmentTree::modify(1,1,seg[0],seg[top[now]],seg[now],1);
        now=f[top[now]];
    }
}

long long solve(int now)
{
    long long res=::res+tot*dis[now];
    while(top[now])
    {
        res-=2LL * SegmentTree::query(1,1,seg[0],seg[top[now]],seg[now]);
        now=f[top[now]];
    }
    return res;
}


signed main()
{
    /*freopen("express.in","r",stdin);*/
    /*freopen("express.out","w",stdout);*/
    cin>>n>>m;
    int u,v,w;
    for(int i=1;i<n;i++)
    {
        cin>>u>>v>>w;
        add(u,v,w);
        add(v,u,w);
    }
    dfs1(1,0); dfs2(1,1);
    SegmentTree::build(1,1,seg[0]);
    while(m--)
    {
        int opt,u;
        cin>>opt>>u;
        if(opt==1)
            modify(u);
        else
            printf("%lld\n",solve(u));
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章