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

Posted 斗奋力努

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了G. Matematical Transformation(树链剖分+线段树)相关的知识,希望对你有一定的参考价值。

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);
        
    

以上是关于G. Matematical Transformation(树链剖分+线段树)的主要内容,如果未能解决你的问题,请参考以下文章

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

sql Transforma linhas em colunas

Transforma dias em数据完成

G. Hits Different

G. 类常量

HDU - 1498 G.题解