bzoj_1036 树链剖分套线段树

bzoj_1036

★★★★   输入文件:bzoj_1036.in   输出文件:bzoj_1036.out   简单对比
时间限制:1 s   内存限制:162 MB

目描述】

一棵树上有n个节点,编号分别为1n,每个节点都有一个权值w。我们将以下面的形式来要求你对这棵树完成一些操作: 

I. CHANGE u t : 把结点u的权值改为

II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值 

III. QSUM u v: 询问从点u到点v的路径上的节点的权值和 

注意:从点u到点v的路径上的节点包括uv本身

入格式】

输入的第一行为一个整数n,表示节点的个数。

接下来n – 1行,每行2个整数ab,表示节点a和节点b之间有一条边相连。

接下来n行,每行一个整数,第i行的整数wi表示节点i的权值。

接下来1行,为一个整数q,表示操作的总数。

接下来q行,每行一个操作,以“CHANGE u t”或者“QMAX u v”或者“QSUM u v”的形式给出。 

对于100%的数据,保证1<=n<=300000<=q<=200000;中途操作中保证每个节点的权值w-3000030000之间。

出格式】

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

入】

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

  

出】

4

1

2

2

10

6

5

6

5

16

#include<bits/stdc++.h>
#define maxn 30005
#define ls (rt<<1)
#define rs (rt<<1|1)
#define mid (l+r>>1)
#define lson ls,l,mid
#define rson rs,mid+1,r
using namespace std;
int n,q;
vector<int> v[maxn];int a[maxn];
int size[maxn],son[maxn],fa[maxn],top[maxn],dep[maxn],dfn[maxn],pos[maxn],cnt;
int sum[maxn<<2],mx[maxn<<2];
void Dfs(int rt){
    size[rt]=1;
    for(int i=0;i<v[rt].size();i++){
        int to=v[rt][i];
        if(!size[to]){
            fa[to]=rt;
            dep[to]=dep[rt]+1;
            Dfs(to);
            size[rt]+=size[to];
            if(size[to]>size[son[rt]]) son[rt]=to;
        }
    }
}
void Dfs(int rt,int tp){
    top[rt]=tp;
    dfn[++cnt]=rt;
    pos[rt]=cnt;
    if(son[rt]) Dfs(son[rt],tp);
    for(int i=0;i<v[rt].size();i++)
        if(!top[v[rt][i]]) Dfs(v[rt][i],v[rt][i]);
}
void Build(int rt,int l,int r){
    if(l==r){
        sum[rt]=mx[rt]=a[dfn[l]];
        return;
    }
    Build(lson);Build(rson);
    sum[rt]=sum[ls]+sum[rs];mx[rt]=max(mx[ls],mx[rs]);
}
void Add(int rt,int l,int r,int posx,int qx){
    if(l==r){
        sum[rt]=qx;
        mx[rt]=qx;
        return;
    }
    if(posx<=mid) Add(lson,posx,qx);
    else Add(rson,posx,qx);
    sum[rt]=sum[ls]+sum[rs];mx[rt]=max(mx[ls],mx[rs]);
}
int Sum(int rt,int l,int r,int s,int t){
    if(s>r||t<l) return 0;
    if(s<=l&&r<=t) return sum[rt];
    return Sum(lson,s,t)+Sum(rson,s,t);
}
int Max(int rt,int l,int r,int s,int t){
    if(s>r||t<l) return -maxn;
    if(s<=l&&r<=t) return mx[rt];
    return max(Max(lson,s,t),Max(rson,s,t));
}
int lca(int x,int y,bool opt){
    int res;
    if(opt) res=0;
    else res=-maxn;
    while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]]) swap(x,y);
        if(opt) res+=Sum(1,1,n,pos[top[x]],pos[x]);
        else res=max(res,Max(1,1,n,pos[top[x]],pos[x]));
        x=fa[top[x]];
    }
    if(dep[x]>dep[y]) swap(x,y);
    if(opt) res+=Sum(1,1,n,pos[x],pos[y]);
        else res=max(res,Max(1,1,n,pos[x],pos[y]));
    return res;
}
int main()
{
    freopen("bzoj_1036.in","r",stdin);
    freopen("bzoj_1036.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<n;i++){
        int x,y;scanf("%d%d",&x,&y);
        v[x].push_back(y);v[y].push_back(x);
    }    
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    Dfs(1);Dfs(1,1);Build(1,1,n);
    scanf("%d",&q);
    while(q--){
        string s;cin>>s;
        if(s[0]=='C'){
            int x,v;
            scanf("%d%d",&x,&v);
            Add(1,1,n,pos[x],v);
        }
        if(s[1]=='M'){
            int x,y;scanf("%d%d",&x,&y);
            printf("%d\n",lca(x,y,0));
        }
        if(s[1]=='S'){
            int x,y;scanf("%d%d",&x,&y);
            printf("%d\n",lca(x,y,1));
        }
    }
    return 0;
}

唉 编程不易啊

考试的时候就写错了两行就爆0了!!!!

 

 

 

 

那个dfn数组  和  pos数组

在从外面进入线段树的时候要用pos

从线段树里调用外面的时候要用dfn

千万要记住!!

 

还有一个地方  就是那个Max函数  如果越界 返回值一定要是一个负无穷   否则万一本来最大值就是一个负数  而你越界了反而返回了一个0 那不就出bug了吗    还是要灵活变通啊!

加油 多练练就好了

ヾ(◍°∇°◍)ノ゙

posted @ 2019-08-09 10:10  DreamingBligo_Tido  阅读(190)  评论(0编辑  收藏  举报