【BZOJ1036】【ZJOI2008】【树的统计count】【树链剖分】

本文详细介绍了一种高效处理树形结构数据查询与更新问题的算法——树链剖分。该算法通过预处理将树分解成一系列链,利用线段树进行加速查询,适用于解决涉及大量节点间最大值及总和查询的问题。

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

Description

一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w。我们将以下面的形式来要求你对这棵树完成一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值 III. QSUM u v: 询问从点u到点v的路径上的节点的权值和 注意:从点u到点v的路径上的节点包括u和v本身

Input

输入的第一行为一个整数n,表示节点的个数。接下来n – 1行,每行2个整数a和b,表示节点a和节点b之间有一条边相连。接下来n行,每行一个整数,第i行的整数wi表示节点i的权值。接下来1行,为一个整数q,表示操作的总数。接下来q行,每行一个操作,以“CHANGE u t”或者“QMAX u v”或者“QSUM u v”的形式给出。 对于100%的数据,保证1<=n<=30000,0<=q<=200000;中途操作中保证每个节点的权值w在-30000到30000之间。

Output

对于每个“QMAX”或者“QSUM”的操作,每行输出一个整数表示要求输出的结果。

Sample Input

4
1 2
2 3
4 1
4 2 1 3
12
QMAX 3 4
QMAX 3 3
QMAX 3 2
QMAX 2 3
QSUM 3 4
QSUM 2 1
CHANGE 1 5
QMAX 3 4
CHANGE 3 6
QMAX 3 4
QMAX 2 4
QSUM 3 4

Sample Output

4
1
2
2
10
6
5
6
5
16
题解:树链剖分模板题。。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,q,sz,cnt,m;
struct use{
    int st,en;
}b[100001];
struct zff{
    int maxx,sum;
}tree[1000001];
int fa[100001][15],size[1000001],deep[100001],point[100001],next[1000001],vis[1000001],pos[1000001],w[1000001],aa,bb;
int belong[1000001],ffpos[1000001];
char ch[10001];
void add(int x,int y)
{
    next[++cnt]=point[x];point[x]=cnt;
    b[cnt].st=x;b[cnt].en=y; 
}
void init()
{
    int a,b;
    scanf("%d",&n);
    for (int i=1;i<=n-1;i++)
    {
         scanf("%d%d",&a,&b);
         add(a,b);add(b,a);
    }
   for (int i=1;i<=n;i++)
      scanf("%d",&w[i]);
}
void build(int x,int l,int r)
{
    int mid;
    if (l==r){tree[x].maxx=w[ffpos[l]];tree[x].sum=w[ffpos[l]];return;}
    mid=(l+r)/2;
    build(x*2,l,mid);
    build(x*2+1,mid+1,r);
    tree[x].maxx=max(tree[x*2].maxx,tree[x*2+1].maxx);
    tree[x].sum=tree[x*2].sum+tree[x*2+1].sum; 
}
void dfs(int x)
{
    size[x]=1;vis[x]=1;
    for (int i=1;i<=14;i++)
     {
      if (deep[x]<(1<<i)) break;
        fa[x][i]=fa[fa[x][i-1]][i-1];
     }
    for (int i=point[x];i;i=next[i])
      {
         if (vis[b[i].en]==1) continue;
         deep[b[i].en]=deep[x]+1;
         fa[b[i].en][0]=x;
         dfs(b[i].en);
         size[x]+=size[b[i].en];
      }
}
void dfs2(int x,int c)
{
    int k(0);
    pos[x]=++sz;
    ffpos[sz]=x;
    belong[x]=c;
    for (int i=point[x];i;i=next[i]) if (size[b[i].en]>size[k]&&deep[b[i].en]>deep[x]) k=b[i].en;
    if (k==0) return;
    dfs2(k,c);
    for (int i=point[x];i;i=next[i]) if (deep[b[i].en]>deep[x]&&b[i].en!=k) dfs2(b[i].en,b[i].en);
}
int  lca(int x,int y)
{
    int t;
    if(deep[x]<deep[y]) swap(x,y);t=deep[x]-deep[y];
    for (int i=0;i<=14;i++) if (t&(1<<i)) x=fa[x][i];
     for (int i=14;i>=0;i--)
       if (fa[x][i]!=fa[y][i]) {x=fa[x][i];y=fa[y][i];}
     if (x==y) return x;else return fa[x][0];
}
void insert(int k,int l,int r)
{
    int mid;
    if(l==r&&l==pos[aa]){tree[k].maxx=w[aa];tree[k].sum=w[aa];return;}
    mid=(l+r)/2;
    if (pos[aa]<=mid) insert(k*2,l,mid);
    else insert(k*2+1,mid+1,r);
    tree[k].maxx=max(tree[2*k].maxx,tree[2*k+1].maxx);
    tree[k].sum=tree[2*k].sum+tree[2*k+1].sum; 
}
int qsum(int k,int l,int r,int xx,int yy)
{
    int mid,zsum=0;
    if (xx<=l&&r<=yy) return tree[k].sum;
    mid=(l+r)/2;
    if (xx<=mid) zsum+=qsum(k*2,l,mid,xx,yy);
    if (yy>mid)  zsum+=qsum(k*2+1,mid+1,r,xx,yy);
    return zsum;
}
int qmax(int k,int l,int r,int xx,int yy)
{
    int mid,zmax=-999999999;
    if (xx<=l&&r<=yy) 
      {
        return tree[k].maxx;
      }
    mid=(l+r)/2;
    if (xx<=mid) zmax=max(zmax,qmax(k*2,l,mid,xx,yy));
    if (yy>mid)  zmax=max(zmax,qmax(k*2+1,mid+1,r,xx,yy));
    return zmax; 
}
int ssum(int x,int y)
{
   int zsum(0);
   while (belong[x]!=belong[y])
     {
        zsum+=qsum(1,1,n,pos[belong[x]],pos[x]);
        x=fa[belong[x]][0];
     }
   zsum+=qsum(1,1,n,pos[y],pos[x]);
   return zsum; 
} 
int smax(int x,int y)
{
   int zmax(-999999999);
   while (belong[x]!=belong[y])
     {
        zmax=max(zmax,qmax(1,1,n,pos[belong[x]],pos[x]));
        x=fa[belong[x]][0];
     }
   zmax=max(zmax,qmax(1,1,n,pos[y],pos[x]));
   return zmax;
}
void solve()
{
    int t;
    build(1,1,n);
    scanf("%d",&m);
    for(int i=1;i<=m;i++) 
    {
        scanf("%s%d%d",ch,&aa,&bb);
        if (ch[0]=='C'){w[aa]=bb;insert(1,1,n);}
        else
          {
             t=lca(aa,bb);
             if (ch[1]=='M') printf("%d\n",max(smax(aa,t),smax(bb,t)));
             else printf("%d\n",ssum(aa,t)+ssum(bb,t)-w[t]);     
          } 
    }
} 
int main()
{
    init();
    dfs(1);
    dfs2(1,1);
    solve();
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值