hdu6547 / 2019中国大学生程序设计竞赛-女生专场 · Tree ·树链剖分+线段树

昨天刚学了树链剖分,突然想起来去年第一次参加比赛的时候,也有类似的题目没有做出来,当时比较懒惰,赛后没补,现在翻回来补补题,然后发现这题居然是道简单题,吐血
可惜了,当年要是勤快一点,提前学了树链剖分,没准就金了,
遂记此题解以作警示,不可再让机会白白溜走了

题解

很基础的树链剖分+线段树

树链剖分入门学习

涉及到开根号只能单点修改,这里要注意一下,每次都单点修改会T,特判一下当前点的权值是否为1或者为0,避免重复操作


在这里插入图片描述


#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
const int N=2e5+10;

ll a[N];
int n,q;
struct egde{
    int to,next;
}e[N];
int head[N],tot;
void add(int u,int v){
    e[++tot]={v,head[u]};
    head[u]=tot;
}

int f[N],dep[N],son[N],sz[N];
void dfs1(int u,int fa){
    dep[u]=dep[fa]+1;
    f[u]=fa;
    sz[u]=1;
    for(int i=head[u];i;i=e[i].next){
        int v=e[i].to;
        if(v!=fa){
            dfs1(v,u);
            sz[u]+=sz[v];
            if(sz[v]>sz[son[u]])
                son[u]=v;
        }
    }
}

int id[N],top[N],dfn;
ll w[N];
void dfs2(int u,int t){
    top[u]=t;
    id[u]=++dfn;
    w[dfn]=a[u];
    if(son[u])dfs2(son[u],t);
    for(int i=head[u];i;i=e[i].next){
        int v=e[i].to;
        if(v!=f[u] && v!=son[u])
            dfs2(v,v);
    }
}

int lazy[N];//记录要更新多少次
bool flag[N];//判断区间值是否为1或0 防T
ll t[N*4];
void pushup(int rt){
    t[rt]=t[rt<<1]+t[rt<<1|1];
    flag[rt]=(flag[rt<<1]&&flag[rt<<1|1]);
}
void build(int l,int r,int rt){
    if(l==r){
        t[rt]=w[l];
        return;
    }
    int mid=l+r>>1;
    build(lson);
    build(rson);
    pushup(rt);
}
void update(int L,int R,int l,int r,int rt){//暴力开根号
    if(flag[rt])return;//1无法再更新 0区间内有点还可以修改
    if(l==r){//单点修改
        t[rt]=(ll)sqrt(t[rt]);
        if(t[rt]==0||t[rt]==1) flag[rt]=1;
        return;
    }
    int mid=l+r>>1;
    if(L<=mid)update(L,R,lson);
    if(R>mid)update(L,R,rson);
    pushup(rt);
}

ll query(int L,int R,int l,int r,int rt){
    if(L<=l &&r<=R){
        return t[rt];
    }
    ll res=0;
    int mid=l+r>>1;
    if(L<=mid)res+=query(L,R,lson);
    if(R>mid)res+=query(L,R,rson);
    return res;
}

void updRange(int u,int v){
    while(top[u]!=top[v]){
        if(dep[top[u]] < dep[top[v]])
            swap(u,v);//u链顶深
        update(id[top[u]],id[u],1,n,1);
        u=f[top[u]];
    }
    if(dep[u]>dep[v])swap(u,v);
    update(id[u],id[v],1,n,1);
}
ll qRange(int u,int v){
    ll res=0;
    while(top[u]!=top[v]){
        if(dep[top[u]]<dep[top[v]])swap(u,v);
        res+=query(id[top[u]],id[u],1,n,1);
        u=f[top[u]];
    }
    if(dep[u]>dep[v])
        swap(u,v);
    res+=query(id[u],id[v],1,n,1);
    return res;
}

int main(){
    ios::sync_with_stdio(0);

    cin>>n>>q;
    for (int i = 1; i <= n; ++i) {
        cin>>a[i];
    }
    for (int i = 1,u,v; i < n; ++i) {
        cin>>u>>v;
        add(u,v);
        add(v,u);
    }
    dfs1(1,0);
    dfs2(1,1);
    build(1,n,1);
    for (int i = 1,op,u,v; i <= q; ++i) {
        cin>>op>>u>>v;
        if(op==0){
            updRange(u,v);
        }else{
            cout <<qRange(u,v) << endl;
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值