【codevs2460】【BZOJ1036】树的统计count,第一次的树链剖分

传送门1
传送门2
写在前面:昨天一个小时能A三道题,今天三个小时A一道题
思路:比较裸的树链剖分,只不过是把边权值变成了点权值,开一个数组记录边和点的关系
注意:
1.比较最大值时ans设为-∞,不要设成0!
2.据本人经验,一个结构体有助于身体健康,两个就不行了……
代码:

#include<bits/stdc++.h>
#define mul(x) (x<<1)
#define Minn -0x7fffffff
using namespace std;
int n,q,tot,x,y,cnt;
int first[30010],pre[30010];
char ch[10];
struct point
{
    int top,siz,dep,fa,son,w,hash;
}a[30010];
struct os
{
    int u,v,next;
}e[60010];
struct node
{
    int sum,maxn;
}tree[120010];
void add(int x,int y)
{
    e[++tot].u=x;
    e[tot].v=y;
    e[tot].next=first[x];
    first[x]=tot;
}
void dfs1(int now)
{
    a[now].siz=1;
    for (int i=first[now];i;i=e[i].next)
    if (e[i].v!=a[now].fa)
    {
        a[e[i].v].fa=now;
        a[e[i].v].dep=a[now].dep+1;
        dfs1(e[i].v);
        if (a[e[i].v].siz>a[a[now].son].siz)
        a[now].son=e[i].v;
        a[now].siz+=a[e[i].v].siz;
    }
}
void dfs2(int now,int tp)
{
    a[now].hash=++cnt;
    a[now].top=tp;
    pre[cnt]=now;
    if (a[now].son)
    dfs2(a[now].son,tp);
    for (int i=first[now];i;i=e[i].next)
    if (e[i].v!=a[now].son&&e[i].v!=a[now].fa)
    dfs2(e[i].v,e[i].v);
}
void build(int now,int begin,int end)
{
    if (begin==end) {tree[now].sum=tree[now].maxn=a[pre[begin]].w;return;}
    int mid=(begin+end)>>1;
    build(mul(now),begin,mid);
    build(1+mul(now),mid+1,end);
    tree[now].maxn=max(tree[mul(now)].maxn,tree[1+mul(now)].maxn);
    tree[now].sum=tree[mul(now)].sum+tree[1+mul(now)].sum;
}
void update(int now,int begin,int end,int pos,int num)
{
    if (begin==end) {tree[now].sum=tree[now].maxn=num;return;}
    int mid=(begin+end)>>1;
    if (pos<=mid) update(mul(now),begin,mid,pos,num);
    else update(1+mul(now),mid+1,end,pos,num);
    tree[now].maxn=max(tree[mul(now)].maxn,tree[1+mul(now)].maxn);
    tree[now].sum=tree[mul(now)].sum+tree[1+mul(now)].sum;
}
int get_max(int now,int begin,int end,int l,int r)
{
    if (l<=begin&&end<=r) return tree[now].maxn;
    int mid=(begin+end)>>1,ans=Minn;
    if (l<=mid) ans=max(ans,get_max(mul(now),begin,mid,l,r));
    if (r>mid) ans=max(ans,get_max(mul(now)+1,mid+1,end,l,r));
    return ans;
}
int get_sum(int now,int begin,int end,int l,int r)
{
    if (l<=begin&&end<=r) return tree[now].sum;
    int mid=(begin+end)>>1,ans=0;
    if (l<=mid) ans+=get_sum(mul(now),begin,mid,l,r);
    if (r>mid) ans+=get_sum(mul(now)+1,mid+1,end,l,r);
    return ans;
}
int solve1(int l,int r)
{
    int ans=Minn,f1=a[l].top,f2=a[r].top;
    while (f1!=f2)
    {
        if (a[f1].dep<a[f2].dep) swap(f1,f2),swap(l,r);
        ans=max(ans,get_max(1,1,cnt,a[f1].hash,a[l].hash));
        l=a[f1].fa;f1=a[l].top;
    }
    if (a[l].dep>a[r].dep) swap(l,r);
    return max(ans,get_max(1,1,cnt,a[l].hash,a[r].hash));
}
int solve2(int l,int r)
{
    int ans=0,f1=a[l].top,f2=a[r].top;
    while (f1!=f2)
    {
        if (a[f1].dep<a[f2].dep) swap(f1,f2),swap(l,r);
        ans+=get_sum(1,1,cnt,a[f1].hash,a[l].hash);
        l=a[f1].fa;f1=a[l].top;
    }
    if (a[l].dep>a[r].dep) swap(l,r);
    return ans+get_sum(1,1,cnt,a[l].hash,a[r].hash);
}
main()
{
    scanf("%d",&n);
    for (int i=1;i<n;i++)
    scanf("%d%d",&x,&y),
    add(x,y),
    add(y,x);
    for (int i=1;i<=n;i++) scanf("%d",&a[i].w);
    dfs1(1);
    dfs2(1,1);
    build(1,1,cnt); 
    scanf("%d",&q);
    while (q--)
    {
        scanf("%s",ch);
        scanf("%d%d",&x,&y);
        if (ch[0]=='C')
        update(1,1,cnt,a[x].hash,y);
        else
        {
            if (ch[1]=='M') printf("%d\n",solve1(x,y));
            else printf("%d\n",solve2(x,y));
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值