G. Matematical Transformation(树链剖分+线段树)

23 篇文章 0 订阅
1 篇文章 0 订阅

G. Matematical Transformation(树链剖分+线段树)

G. Matematical Transformation
在这里插入图片描述
题意:
一颗有n个节点的有根树,根节点是1,初始每个节点的值为0
现在进行q次操作
操作0: 0 u v 输出节点u到节点v的路径和
操作1: 1 u v k 对节点u的子树,每个节点的价值加上 v + k ∗ d v+k*d v+kd,d为该节点i到节点u的深度差 ( d [ i ] − d [ u ] ) (d[i]-d[u]) (d[i]d[u])
思路:
树链剖分套路题。
每次进行操作0,其实就是树链剖分中的求路径和(链的长度)
每次进行操作1,我们知道对于某个需要修改的节点i来说,
其值会加上 v + k ∗ d = v + k ∗ ( d e p [ i ] − d e p [ u ] ) = ( v + k ∗ d e p [ u ] ) − ( k ∗ d e p [ i ] ) v+k*d=v+k*(dep[i]-dep[u])=(v+k*dep[u])-(k*dep[i]) v+kd=v+k(dep[i]dep[u])=(v+kdep[u])(kdep[i])

我们发现,对于每次修改:
第一个括号 ( v + k ∗ d e p [ u ] ) (v+k*dep[u]) (v+kdep[u]),对于u的整颗子树(要修改区间)的节点是一个定值。
对于线段树的每个节点,我们可以单独维护一个标记 ( a d d s ) (adds) (adds)。来表示每次区间修改的定值情况

第二个括号 ( k ∗ d e p [ i ] ) (k*dep[i]) (kdep[i]),对于u的整颗子树(要修改区间)的节点是一个不定值。
但是单独对于每个节点来说,每次修改都是自己的深度 d e p [ i ] dep[i] dep[i],乘上修改的 − k -k k
对于线段树中的每个节点,我们可以另外单独维护一个标记 ( a d d k ) (addk) (addk),来记录每次区间修改的不定值情况

综上,我们发现每次修改都与深度有关。
所以对于线段树的每个节点,我们同时还需要维护一个区间节点的深度和 ( s u m d e p ) (sumdep) (sumdep),当然区间和 ( s u m ) (sum) (sum)肯定不能少。

注意一点,答案爆掉longlong了,要用__int128。

最后,码上树链剖分模板就能自动ac。

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=3e5+5,M=2*N;
int n,q;
int to[M],ne[M],fir[N],idx;
int id[N],nd[N],cnt;
int dep[N],sz[N],top[N],son[N],fa[N];
struct node{
    int l,r;
    __int128 adds,addk;
    __int128 sumdep,sum;
}tr[N<<2];

void print(__int128 x){
    if(x<0){
        x=-x;
        putchar('-');
    }
    if(x>9) print(x/10);
    putchar(x%10+'0');
}

//链式前
void add(int u,int v){to[idx]=v,ne[idx]=fir[u],fir[u]=idx++;}

//树链剖分模板
void dfs1(int u,int father,int depth){
    sz[u]=1,fa[u]=father,dep[u]=depth;
    for(int i=fir[u];~i;i=ne[i]){
        int v=to[i];
        if(v==father) continue;
        dfs1(v,u,depth+1);
        sz[u]+=sz[v];
        if(sz[son[u]]<sz[v]) son[u]=v;
    }
}

void dfs2(int u,int t){
    id[u]=++cnt,nd[cnt]=dep[u],top[u]=t;
    if(!son[u]) return;
    dfs2(son[u],t);
    for(int i=fir[u];~i;i=ne[i]){
        int v=to[i];
        if(v==fa[u]||v==son[u]) continue;
        dfs2(v,v);
    }
}

//线段树模板
void build(int u,int l,int r){
    tr[u]={l,r,0,0,0,0};
    if(l==r){
        tr[u].sumdep=nd[l];
        return;
    }
    int mid=(l+r)>>1;
    build(u<<1,l,mid),build(u<<1|1,mid+1,r);
    tr[u].sumdep=tr[u<<1].sumdep+tr[u<<1|1].sumdep;
}

void pushdown(int u){
    auto &root=tr[u],&left=tr[u<<1],&right=tr[u<<1|1];
    if(root.addk||root.adds){
        left.adds+=root.adds;
        left.addk+=root.addk;
        left.sum+=(left.r-left.l+1)*root.adds+left.sumdep*root.addk;
        right.adds+=root.adds;
        right.addk+=root.addk;
        right.sum+=(right.r-right.l+1)*root.adds+right.sumdep*root.addk;
        root.adds=root.addk=0;
    }
}

void modify(int u,int l,int r,__int128 tags,__int128 tagk){
    if(l<=tr[u].l&&tr[u].r<=r){
        tr[u].adds+=tags;
        tr[u].addk+=tagk;
        tr[u].sum+=(tr[u].r-tr[u].l+1)*tags+tr[u].sumdep*tagk;
        return;
    }
    pushdown(u);
    int mid=(tr[u].l+tr[u].r)>>1;
    if(l<=mid) modify(u<<1,l,r,tags,tagk);
    if(r>mid) modify(u<<1|1,l,r,tags,tagk);
    tr[u].sum=tr[u<<1].sum+tr[u<<1|1].sum; 
}

//修改子树
void modify_tree(int u,__int128 tags,__int128 tagk){
	modify(1,id[u],id[u]+sz[u]-1,tags,tagk);
}

__int128 query(int u,int l,int r){
    if(l<=tr[u].l&&tr[u].r<=r) return tr[u].sum;
    pushdown(u);
    int mid=(tr[u].l+tr[u].r)>>1;
    __int128 sum=0;
    if(l<=mid) sum+=query(u<<1,l,r);
    if(r>mid) sum+=query(u<<1|1,l,r);
    return sum;
}

//查询路径
__int128 query_path(int u,int v){
    __int128 res=0;
    while(top[u]!=top[v]){
        if(dep[top[u]]<dep[top[v]]) swap(u,v);
        res+=query(1,id[top[u]],id[u]);
        u=fa[top[u]];
    }
    if(dep[u]<dep[v]) swap(u,v);
    res+=query(1,id[v],id[u]);
    return res;
}

signed main(){
    scanf("%lld",&n);
    for(int i=1;i<=n;i++) fir[i]=-1;
    for(int i=1;i<n;i++){
        int u,v; scanf("%lld%lld",&u,&v);
        add(u,v),add(v,u);
    }
    dfs1(1,0,1);
    dfs2(1,1);
    build(1,1,n);
    scanf("%lld",&q);
    while(q--){
        int op; scanf("%lld",&op);
        if(op==0){
            int u,v; scanf("%lld%lld",&u,&v);
            __int128 ans=query_path(u,v);
            print(ans);
            puts("");
        }
        else{
            int u,v,k; scanf("%lld%lld%lld",&u,&v,&k);
            modify_tree(u,v-k*dep[u],k);
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值