2017年浙江中医药大学大学生程序设计竞赛-A:不存在的树(树链剖分)

时间限制:C/C++ 2秒,其他语言4秒
空间限制:C/C++ 131072K,其他语言262144K
64bit IO Format: %lld

题目描述

kotomi 有一棵树。树上有n个房子,编号1-n,每个房子有一个快乐值。
kotomi想知道从a房子到b房子路径上的最大快乐值或者路径山疙瘩快乐值的和。
并且kotomi可以改变任意一个房子的快乐值。
具体如下
(1) 0 a b:查询a到b路径上的最大快乐值(包含a和b)
(2) 1 a b:查询a到b路径上的所有房子快乐值的和。(包含a和b)
(3) 2 x y:将编号为x的房子的快乐值改为y。

输入描述:

  
  
多组测试数据
第一行有两个整数n,q。表示有n个房子,q次操作。
第二行有n个整数v1,v2...vn,表示编号为i的房子的快乐值vi
接下来n-1行,每行两个整数u,v,表示编号为u和编号为v的房子之间有一条直接路径。
接下来p行,每行开头一个整数(0,1或2)表述操作类型,每个操作后有两个整数。

输出描述:

对于操作为0和1的输出对应的值。
示例1

输入

6 10
2 5 9 10 36 5
1 2
1 3
1 4
2 5
2 6
0 1 4
0 1 6
1 5 6
1 3 6
1 6 3
2 3 10
1 5 3
0 4 5
2 5 100
1 5 4

输出

10
5
46
21
21
53
36
117
思路:树链剖分模版题。

#include<bits/stdc++.h>
using namespace std;
const int MAX=4e4;
vector<int>e[MAX];
int a[MAX],son[MAX],siz[MAX],d[MAX],fa[MAX],tp[MAX],val[MAX],num[MAX],all;
void dfs1(int k,int f,int dep)
{
    d[k]=dep;  //k节点的深度
    fa[k]=f;   //k节点的父亲
    siz[k]=1;  //以k为根的子树的节点个数
    son[k]=0;  //记录重儿子
    for(int i=0;i<e[k].size();i++)
    {
        if(e[k][i]==f)continue;
        dfs1(e[k][i],k,dep+1);
        siz[k]+=siz[e[k][i]];
        if(siz[son[k]]<siz[e[k][i]])son[k]=e[k][i];//更新重儿子
    }
}
void dfs2(int k,int top)
{
    tp[k]=top;       //k所在的链的顶端
    num[k]=++all;
    val[all]=a[k];
    if(son[k])dfs2(son[k],top);//优先遍历重儿子
    for(int i=0;i<e[k].size();i++)
    {
        if(e[k][i]==fa[k]||e[k][i]==son[k])continue;
        dfs2(e[k][i],e[k][i]);
    }
}
struct lenka
{
    int l,r,ma,sum;
}A[MAX<<2];
void build(int k,int l,int r)
{
    A[k].l=l,A[k].r=r;
    if(l==r)
    {
        A[k].ma=val[l];
        A[k].sum=val[l];
        return;
    }
    build(2*k,l,(l+r)/2);
    build(2*k+1,(l+r)/2+1,r);
    A[k].ma=max(A[2*k].ma,A[2*k+1].ma);
    A[k].sum=A[2*k].sum+A[2*k+1].sum;
}
void change(int k,int x,int y)
{
    if(x==A[k].l&&x==A[k].r){A[k].ma=A[k].sum=y;return;}
    if(x<=A[2*k].r)change(2*k,x,y);
    else change(2*k+1,x,y);
    A[k].ma=max(A[2*k].ma,A[2*k+1].ma);
    A[k].sum=A[2*k].sum+A[2*k+1].sum;
}
int ask(int k,int x,int y,int tag)
{
    if(x==A[k].l&&y==A[k].r)return tag?A[k].sum:A[k].ma;
    if(y<=A[2*k].r)return ask(2*k,x,y,tag);
    else if(x>=A[2*k+1].l)return ask(2*k+1,x,y,tag);
    else
    {
        if(tag)return ask(2*k,x,A[2*k].r,tag)+ask(2*k+1,A[2*k+1].l,y,tag);
        else return max(ask(2*k,x,A[2*k].r,tag),ask(2*k+1,A[2*k+1].l,y,tag));
    }
}
long long get(int x,int y,int tag)
{
    long long ans=-1e15,sum=0;
    while(tp[x]!=tp[y])//逐渐向一条链上靠拢并同时更新答案
    {
        if(d[tp[x]]<d[tp[y]])swap(x,y);
        if(tag==0)ans=max(ans,(long long)ask(1,num[tp[x]],num[x],0));
        else sum+=(long long)ask(1,num[tp[x]],num[x],1);
        x=fa[tp[x]];
    }
    if(d[x]>d[y])swap(x,y);
    if(tag==0)ans=max(ans,(long long)ask(1,num[x],num[y],0));
    else sum+=(long long)ask(1,num[x],num[y],1);
    return tag?sum:ans;
}
int main()
{
    int n,m;
    while(cin>>n>>m)
    {
        for(int i=1;i<=n;i++)e[i].clear();
        for(int i=1;i<=n;i++)cin>>a[i];
        for(int i=1;i<n;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            e[x].push_back(y);
            e[y].push_back(x);
        }
        all=0;
        dfs1(1,0,1);
        dfs2(1,1);
        build(1,1,all);
        while(m--)
        {
            int op,x,y;
            scanf("%d%d%d",&op,&x,&y);
            if(op==0)printf("%lld\n",get(x,y,0));
            else if(op==1)printf("%lld\n",get(x,y,1));
            else change(1,num[x],y);
        }
    }
    return 0;
}




  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值