洛谷 P2590 [ZJOI2008]树的统计/[BZOJ 1036]树的统计Count

洛谷 P2590 [ZJOI2008]树的统计

[BZOJ 1036]树的统计Count

线段树+树链剖分

题目描述

一棵树上有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本身

输入输出格式

输入格式:

 

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

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

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

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

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

 

输出格式:

 

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

巨长代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>

using namespace std;

int sum1[1200010],a1[100010],max1[1200010],b[1200010];
int sz[1000010],w[1000010],id[1000010],fa[1000010],top[1000010],h[1000010];

int N=0,n;

void build(int l,int r,int now);
void update(int L,int R,int b,int now,int l,int r);//l gai wei b
int querysum(int L,int R,int l,int  r,int now);
int querymax(int L,int R,int l,int  r,int now);
void dfs1(int x);
void dfs2(int x);
int tquerymax(int x,int y);
int tquerysum(int x,int y);

vector<int> a[30010];

void dfs1(int x)
{
    sz[x]=1;
    w[x]=0;
    int sz1=a[x].size();
    for(int i=0;i<sz1;i++)
    {
        int y=a[x][i];//寻找下一条边
        if(y==fa[x]) continue;//
        fa[y]=x;
        h[y]=h[x]+1;//y为x深度+1
        dfs1(y);
        sz[x]+=sz[y];//更新y的子树大小
        if(sz[y]>sz[w[x]]) w[x]=y;
    }
}
void dfs2(int x)
{
    id[x] = ++N;//给点编号
    if(w[x])//若x为重儿子
    {
        top[w[x]]=top[x];//把重儿子的所在重链的开头归到X
        dfs2(w[x]);
    } 
    int sz1=a[x].size();
    for(int i=0;i<sz1;++i)
    {
        int y=a[x][i];
        if(y==fa[x]||y==w[x]) continue;//若y儿子是自己
        top[y]=y;//新建一条重链
        dfs2(y);
    }
} 
void build(int l,int r,int now)
{
    if(l==r)//到达叶子节点
    {
        sum1[now]=b[l];//因为只有一个数所以自己的 sum和 max 都是自己
        max1[now]=b[l]; 
        return ;
    }
    int mid=(l+r)>>1;
    build(l,mid,now*2);
    build(mid+1,r,now*2+1);
    sum1[now]=sum1[now<<1]+sum1[now<<1|1];//维护线段树
    max1[now]=max(max1[now<<1],max1[now<<1|1]);
}
void update(int L,int R,int b,int now,int l,int r)//l gai wei b
{
    if(l==L&&R==r)//区间查询(转单点查询)的写法 l,r可以任选一个
    {
        sum1[now]=b;//更新
         max1[now]=b; 
         return ;
    }
    int mid=(l+r)>>1;
    if(L<=mid) update(L,R,b,now<<1,l,mid);
    else update(L,R,b,now<<1|1,mid+1,r);
    sum1[now]=sum1[now<<1]+sum1[now<<1|1];//维护线段树
    max1[now]=max(max1[now<<1],max1[now<<1|1]);
}
int tquerymax(int x,int y)
{
    int ans=-1000000;
    while(top[x]!=top[y])
    {
        if(h[top[x]]<h[top[y]]) swap(x,y);//
        ans=max(ans,querymax(id[top[x]],id[x],1,n,1));//从重链开头到当前节点 (编号)
        x=fa[top[x]];//寻找重链开头的父亲
    }
    if(h[x]<h[y]) swap(x,y);
    ans=max(ans,querymax(id[y],id[x],1,n,1));
    return ans;
}
int tquerysum(int x,int y)
{
    int ans=0;
    while(top[x]!=top[y])
    {
        if(h[top[x]]<h[top[y]]) swap(x,y);
        int l=id[top[x]],r=id[x];
        ans+=querysum(l,r,1,n,1);
        x=fa[top[x]];
    }
    if(h[x]<h[y]) swap(x,y);
    ans+=querysum(id[y],id[x],1,n,1);
    return ans; 
}
int querysum(int L,int R,int l,int r,int now)
{
    if(l>=L&&r<=R) return sum1[now];//当前操作区间小于查询区间 直接返回值
    long long mid=(l+r)>>1;
    int ans=0;
    if(L<=mid) ans+=querysum(L,R,l,mid,now<<1);
    if(R>mid) ans+=querysum(L,R,mid+1,r,now<<1|1);
    return ans;
}
int querymax(int L,int R,int l,int  r,int now)
{
    if(R<l|L>r) return -1000000;//当前操作区间小于查询区间 直接返回值 注意本体有负数 ans不能为0
    //置为比-30000小的数
    if(L<=l&&r<=R) return max1[now];
    int mid=(l+r)>>1;
    return max(querymax(L,R,l,mid,now<<1),querymax(L,R,mid+1,r,now<<1|1));
}
int main()
{
    int m,k,c,ee,u,x,y,l,r;
    char s[20];
    cin>>n;
    for(int i=0;i<n-1;i++)
    {
        scanf("%d%d",&x,&y);
        a[x].push_back(y);
        a[y].push_back(x);
    }
    for (int i=1;i<=n;++i) scanf("%d",&a1[i]);
    h[1]=1;//第一个点深度为1
    dfs1(1);//查找重儿子
    top[1]=1;//第一条重链开头是1
    dfs2(1);//建链
    for(int i=1;i<=n;i++) b[id[i]]=a1[i];
    build(1,n,1);//建立线段树
    cin>>m;
    for(int i=1;i<=m;i++)
    {
        scanf("%s",&s);
        if(s[0]=='C')//把节点u的值更新为c
        {
            scanf("%d%d",&u,&c);
            update(id[u],id[u],c,1,1,n);
        }
        else if(s[1]=='M')
        {
            scanf("%d%d",&l,&r);
            int f= tquerymax(l,r);//查询[l,r]的最大值
            printf("%d\n",f);
        }
        else
        {
            scanf("%d%d",&l,&r);
            int f= tquerysum(l,r);//查询[l,r]的和
            printf("%d\n",f);
        }
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/thj0305/p/9496583.html