BZOJ 1036 [ZJOI2008]树的统计Count 树链剖分

Description

  一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w。我们将以下面的形式来要求你对这棵树完成
一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值 I
II. 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


这题选择了树链剖分来解题。
给大家推荐一个同学推荐给我的树链剖分的好论文:

Code:

#include<bits/stdc++.h>
#define N 30005
using namespace std;
int Ecnt,cnt,root,n,head[N],vis[N];
struct ty{
    int next,to;
}E[N*2];
struct ttyy{
    int pre,deep,sz,son,tid,top;
}tree[N];
struct lT{
    int maxx,sum;
}T[N*4];
void up(int u){
    T[u].maxx=max(T[u<<1].maxx,T[u<<1|1].maxx);
    T[u].sum=T[u<<1].sum+T[u<<1|1].sum;
}
void add(int u,int v){
    E[++Ecnt].next=head[u];
    E[Ecnt].to=v;
    head[u]=Ecnt;
}
void dfs1(int x,int father,int depth){
    vis[x]=1;
    tree[x].pre=father;
    tree[x].deep=depth;
    tree[x].sz=1; tree[x].son=0;
    int maxsize=0;
    for (int i=head[x];i;i=E[i].next){
        int j=E[i].to;
        if (vis[j]) continue;
        dfs1(j,x,depth+1);
        tree[x].sz+=tree[j].sz;
        if (tree[j].sz>maxsize){
            maxsize=tree[j].sz;
            tree[x].son=j;
        }
    }
}
void dfs2(int x,int ancestor){
    vis[x]=1;
    tree[x].tid=++cnt;
    tree[x].top=ancestor;
    if (tree[x].son && (!vis[tree[x].son])) dfs2(tree[x].son,ancestor);
    for (int i=head[x];i;i=E[i].next){
        int j=E[i].to;
        if (vis[j]) continue;
        dfs2(j,j);
    }
}
void changee(int id,int l,int r,int goal,int num){
    if (l==r){
        T[id].sum=T[id].maxx=num;
        return;
    }
    int mid=(l+r)>>1;
    if (goal<=mid) changee(id<<1,l,mid,goal,num);
        else    changee(id<<1|1,mid+1,r,goal,num);
    up(id);
}
int querysum(int id,int l,int r,int gl,int gr){
    if (l>=gl && r<=gr) return T[id].sum;
    int mid=(l+r)>>1;
    if (gr<=mid) return querysum(id<<1,l,mid,gl,gr); else
    if (gl>mid)  return querysum(id<<1|1,mid+1,r,gl,gr);
   		else return querysum(id<<1,l,mid,gl,mid)+querysum(id<<1|1,mid+1,r,mid+1,gr);
}
int querymax(int id,int l,int r,int gl,int gr){
    if (l>=gl && r<=gr) return T[id].maxx;
    int mid=(l+r)>>1;
    if (gr<=mid) return querymax(id<<1,l,mid,gl,gr); else
    if (gl>mid)  return querymax(id<<1|1,mid+1,r,gl,gr);
   		else return max(querymax(id<<1,l,mid,gl,mid),querymax(id<<1|1,mid+1,r,mid+1,gr));
}
int solvesum(int u,int v){
    int gg=0;
    while (tree[u].top!=tree[v].top){
        if (tree[tree[u].top].deep<tree[tree[v].top].deep) swap(u,v);
        gg+=querysum(1,1,n,tree[tree[u].top].tid,tree[u].tid);
        u=tree[tree[u].top].pre;
    }
    if (tree[u].deep>tree[v].deep) swap(u,v);
    gg+=querysum(1,1,n,tree[u].tid,tree[v].tid);
    return gg;
}
int solvemax(int u,int v){
    int gg=-100000;
    while (tree[u].top!=tree[v].top){
        if (tree[tree[u].top].deep<tree[tree[v].top].deep) swap(u,v);
        gg=max(gg,querymax(1,1,n,tree[tree[u].top].tid,tree[u].tid));
        u=tree[tree[u].top].pre;
    }
    if (tree[u].deep>tree[v].deep) swap(u,v);
    gg=max(gg,querymax(1,1,n,tree[u].tid,tree[v].tid));
    return gg;
}
int main(){
    scanf("%d",&n);
    int x,y; Ecnt=0;
    for (int i=1;i<n;i++){
        scanf("%d%d",&x,&y);
        add(x,y); add(y,x);
    }
    root=1; cnt=0;
    memset(vis,0,sizeof(vis));    dfs1(root,0,0);
    memset(vis,0,sizeof(vis));    dfs2(root,root);
    for (int i=1;i<=n;i++){
        scanf("%d",&x);
        changee(1,1,n,tree[i].tid,x);
    }
    int m;
    scanf("%d",&m);
    while (m--){
        char s[10];
        int x,y;
        scanf("%s%d%d",s,&x,&y);
        if (s[0]=='C')    changee(1,1,n,tree[x].tid,y);
            else{
                if (s[1]=='M')
                    printf("%d\n",solvemax(x,y));
                        else    printf("%d\n",solvesum(x,y));
            }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值