bzoj 3221: [Codechef FEB13] Obserbing the tree树上询问 (可持久化线段树+树链剖分)

本文介绍了一种在树形结构中进行区间修改和查询的方法,通过维护每个区间的首项和公差,实现高效的区间更新及求和操作。特别适用于需要回溯历史状态的场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

传送门

题目大意:给出一个n个节点的树
操作1:x,y a,k 将x到y路径上的点加入一个首项为a,公差为k等差数列
操作2:x,y 查询路径上的点权和
操作3:x回到第x次操作1后的结果

题解

区间修改区间查询,对于每个区间维护当前区间的首项和公差,以及区间的权值和。
标记永久化,查询的时候将路径上的首项公差加和,最后计入答案即可。
注意区间首项的计算,区间修改时对于右儿子首项应该是 a+max(0,mid+1max(l,ll))k

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 200003
#define LL long long 
using namespace std;
struct data{
    int ls,rs;
    LL a,k,sum;
}tr[N*100];
int n,m,root[N],mp[N],now,sz,cnt,pos[N],fa[N],L[N],R[N],top,top1,L1[N],R1[N],T[N];
int tot,nxt[N],point[N],v[N],belong[N],deep[N],size[N],son[N],last;
void add(int x,int y)
{
    tot++; nxt[tot]=point[x]; point[x]=tot; v[tot]=y;
    tot++; nxt[tot]=point[y]; point[y]=tot; v[tot]=x;
}
void dfs(int x,int f)
{
    deep[x]=deep[f]+1; size[x]=1;
    for (int i=point[x];i;i=nxt[i]){
        if (v[i]==f) continue;
        fa[v[i]]=x;
        dfs(v[i],x);
        size[x]+=size[v[i]];
        if (size[v[i]]>size[son[x]]) son[x]=v[i];
    }
}
void dfs1(int x,int chain)
{
    belong[x]=chain; pos[x]=++sz;
    if (!son[x]) return;
    dfs1(son[x],chain);
    for (int i=point[x];i;i=nxt[i])
     if (v[i]!=fa[x]&&v[i]!=son[x])
      dfs1(v[i],v[i]);
}
LL query(int i,int l,int r,int ll,int rr,LL a,LL k)
{
    if (ll<=l&&r<=rr) {
        LL len=r-l+1;
        LL an=(len-1)*k+a;
        return tr[i].sum+(a+an)*len/2;
    }
    int mid=(l+r)/2;
    LL ans=0;
    if (ll<=mid) ans+=query(tr[i].ls,l,mid,ll,rr,a+tr[i].a,k+tr[i].k);
    if (rr>mid) ans+=query(tr[i].rs,mid+1,r,ll,rr,a+tr[i].a+(LL)max(0,mid+1-l)*(k+tr[i].k),k+tr[i].k);
    return ans;
}
void update(int now,int l,int r)
{
    int ls=tr[now].ls; int rs=tr[now].rs;
    LL len=r-l+1; LL an=tr[now].a+(len-1)*tr[now].k;
    tr[now].sum=(tr[now].a+an)*len/2;
    if (ls) tr[now].sum+=tr[ls].sum;
    if (rs) tr[now].sum+=tr[rs].sum;
}
void insert(int &i,int j,int l,int r,int ll,int rr,LL a,LL v,int t)
{
    if (i<=t) i=++sz,tr[i]=tr[j];
    if (ll<=l&&r<=rr) {
        tr[i].a+=a; 
        tr[i].k+=v;
        update(i,l,r);
        return; 
    }
    int mid=(l+r)/2;
    if (ll<=mid) insert(tr[i].ls,tr[j].ls,l,mid,ll,rr,a,v,t);
    if (rr>mid) insert(tr[i].rs,tr[j].rs,mid+1,r,ll,rr,a+(LL)max(0,mid+1-max(l,ll))*v,v,t);
    update(i,l,r);
}
void solve(int x,int y,LL a,LL k)
{
    top=0,top1=0;
    while (belong[x]!=belong[y]) {
        if (deep[belong[x]]>=deep[belong[y]]) {
          ++top; L[top]=pos[belong[x]]; R[top]=pos[x];
          x=fa[belong[x]];
        }
        else {
            ++top1; L1[top1]=pos[belong[y]]; R1[top1]=pos[y];
            y=fa[belong[y]];
        }
    }
    if (deep[x]>deep[y]) {
        ++top; L[top]=pos[y]; R[top]=pos[x];
    }
    else {
        ++top1; L1[top1]=pos[x]; R1[top1]=pos[y];
    }
    LL head=0;
    for (int i=1;i<=top;i++) {
        LL len=R[i]-L[i]+1;
        head+=len; LL a1=(head-1)*k+a; 
        insert(root[now],root[last],1,n,L[i],R[i],a1,-k,T[now]);
    }
    head++;
    for (int i=top1;i>=1;i--) {
        LL a1=(head-1)*k+a;
        insert(root[now],root[last],1,n,L1[i],R1[i],a1,k,T[now]);
        LL len=R1[i]-L1[i]+1;
        head+=len;
    }
}
LL calc(int x,int y)
{
    LL ans=0;
    while (belong[x]!=belong[y]) {
        if (deep[belong[x]]<deep[belong[y]]) swap(x,y);
        ans+=query(root[now],1,n,pos[belong[x]],pos[x],0,0);
        x=fa[belong[x]];
    }
    if (deep[x]>deep[y]) swap(x,y);
    ans+=query(root[now],1,n,pos[x],pos[y],0,0);
    return ans;
}
int main()
{
    freopen("a.in","r",stdin);
    freopen("my.out","w",stdout);
    scanf("%d%d",&n,&m);
    for (int i=1;i<n;i++){
        int x,y; scanf("%d%d",&x,&y);
        add(x,y);
    }
    dfs(1,0); dfs1(1,1);
    LL ans=0; sz=0;
    for (int i=1;i<=m;i++) {
        char s[3]; 
        scanf("%s",s);
        if (s[0]=='c') {
            LL x,y; LL a,k; 
            scanf("%lld%lld%lld%lld",&x,&y,&a,&k);
            x^=ans; y^=ans;
            last=now;
            now=++cnt; T[now]=sz;
            solve((int)x,(int)y,a,k);
        }
        else if (s[0]=='q') {
            LL x,y; scanf("%lld%lld",&x,&y);
            x^=ans; y^=ans;
            printf("%lld\n",ans=calc((int)x,(int)y));
        }
        else {
            LL x; scanf("%lld",&x); x^=ans;
            now=x; 
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值