【Luogu】P3313旅行(树链剖分)

  题目链接

  动态开点的树链剖分qwq。

  跟小奇的花园一模一样,不做过多讲解。

  

#include<cstdio>
#include<cstring>
#include<cctype>
#include<cstdlib>
#include<algorithm>
#define maxn 100010
#define mid ((l+r)>>1)
#define check(x) if(x==0)    x=++tot;
using namespace std;
inline long long read(){
    long long num=0,f=1;
    char ch=getchar();
    while(!isdigit(ch)){
        if(ch==‘-‘)    f=-1;
        ch=getchar();
    }
    while(isdigit(ch)){
        num=num*10+ch-‘0‘;
        ch=getchar();
    }
    return num*f;
}

struct Edge{
    int next,to;
}edge[maxn*3];
int head[maxn],num;
inline void add(int from,int to){
    edge[++num]=(Edge){head[from],to};
    head[from]=num;
}

int dfn[maxn];
int back[maxn],cnt;
int sum[maxn*100],tot;
int mav[maxn*100];
int root[maxn];
int ls[maxn*100];
int rs[maxn*100];
int top[maxn];
int size[maxn];
int son[maxn];
int deep[maxn];
int father[maxn];
int d[maxn];
int q[maxn];
int n,m;

void find(int x,int fa){
    size[x]=1;deep[x]=deep[fa]+1;
    for(int i=head[x];i;i=edge[i].next){
        int to=edge[i].to;
        if(to==fa)    continue;
        father[to]=x;
        find(to,x);
        size[x]+=size[to];
        if(son[x]==0||size[son[x]]<size[to])    son[x]=to;
    }
}

void unionn(int x,int Top){
    dfn[x]=++cnt;    back[cnt]=x;
    top[x]=Top;
    if(son[x]==0)    return;
    unionn(son[x],Top);
    for(int i=head[x];i;i=edge[i].next){
        int to=edge[i].to;
        if(to==father[x]||to==son[x])    continue;
        unionn(to,to);
    }
}

inline void pushup(int rt){
    sum[rt]=sum[ls[rt]]+sum[rs[rt]];
    mav[rt]=max(mav[ls[rt]],mav[rs[rt]]);
}

void update(int o,int num,int l,int r,int &rt){
    check(rt);
    if(l==r){
        sum[rt]=num;
        mav[rt]=num;
        return;
    }
    if(o<=mid)    update(o,num,l,mid,ls[rt]);
    else        update(o,num,mid+1,r,rs[rt]);
    pushup(rt);
    return;
}

int quemax(int from,int to,int l,int r,int &rt){
    if(rt==0)    return 0;
    if(from<=l&&to>=r)    return mav[rt];
    int ans=0;
    if(from<=mid)    ans=max(ans,quemax(from,to,l,mid,ls[rt]));
    if(to>mid)        ans=max(ans,quemax(from,to,mid+1,r,rs[rt]));
    return ans;
}

int quesum(int from,int to,int l,int r,int &rt){
    if(rt==0)    return 0;
    if(from<=l&&to>=r)    return sum[rt];
    int ans=0;
    if(from<=mid)    ans+=quesum(from,to,l,mid,ls[rt]);
    if(to>mid)        ans+=quesum(from,to,mid+1,r,rs[rt]);
    return ans;
}

int askmax(int from,int to,int val){
    int ans=0;
    while(top[from]!=top[to]){
        if(deep[top[from]]<deep[top[to]])    swap(from,to);
        ans=max(ans,quemax(dfn[top[from]],dfn[from],1,n,root[val]));
        from=father[top[from]];
    }
    if(deep[from]>=deep[to])    swap(from,to);
    ans=max(ans,quemax(dfn[from],dfn[to],1,n,root[val]));
    return ans;
}

int asksum(int from,int to,int val){
    int ans=0;
    while(top[from]!=top[to]){
        if(deep[top[from]]<deep[top[to]])    swap(from,to);
        ans+=quesum(dfn[top[from]],dfn[from],1,n,root[val]);
        from=father[top[from]];
    }
    if(deep[from]>=deep[to])    swap(from,to);
    ans+=quesum(dfn[from],dfn[to],1,n,root[val]);
    return ans;
}

void chancol(int pos,int val){
    update(dfn[pos],0,1,n,root[q[pos]]);
    update(dfn[pos],d[pos],1,n,root[val]);
    q[pos]=val;
}

void channum(int pos,int val){
    update(dfn[pos],val,1,n,root[q[pos]]);
    d[pos]=val;
}

int main(){
    n=read(),m=read();
    for(int i=1;i<=n;++i){
        d[i]=read();q[i]=read();
    }
    for(int i=1;i<n;++i){
        int x=read(),y=read();
        add(x,y);
        add(y,x);
    }
    find(1,1);
    unionn(1,1);
    for(int i=1;i<=n;++i)    update(dfn[i],d[i],1,n,root[q[i]]);
    for(int i=1;i<=m;++i){
        char c[10];
        scanf("%s",c);
        if(c[0]==‘C‘){
            if(c[1]==‘C‘){
                int x=read(),y=read();
                chancol(x,y);
            }
            else{
                int x=read(),y=read();
                channum(x,y);
            }
        }
        else if(c[0]==‘Q‘){
            if(c[1]==‘S‘){
                int x=read(),y=read();
                printf("%d\n",asksum(x,y,q[x]));
            }
            else{
                int x=read(),y=read();
                printf("%d\n",askmax(x,y,q[x]));
            }
        }
    }
    return 0;
}

https://www.luogu.org/problemnew/show/P3313

原文地址:https://www.cnblogs.com/cellular-automaton/p/8352808.html

时间: 2024-10-30 11:46:02

【Luogu】P3313旅行(树链剖分)的相关文章

[luogu P3384] [模板]树链剖分

[luogu P3384] [模板]树链剖分 题目描述 如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节点的值都加上z 操作2: 格式: 2 x y 表示求树从x到y结点最短路径上所有节点的值之和 操作3: 格式: 3 x z 表示将以x为根节点的子树内所有节点值都加上z 操作4: 格式: 4 x 表示求以x为根节点的子树内所有节点值之和 输入输出格式 输入格式: 第一行包含4个正整数

BZOJ 2157 旅行(树链剖分码农题)

写了5KB,1发AC... 题意:给出一颗树,支持5种操作. 1.修改某条边的权值.2.将u到v的经过的边的权值取负.3.求u到v的经过的边的权值总和.4.求u到v的经过的边的权值最大值.5.求u到v经过的边的权值最小值. 基于边权的树链剖分,放在线段树上变成了区间维护问题了,线段树维护4个量min,max,sum,tag就可以了. # include <cstdio> # include <cstring> # include <cstdlib> # include

BZOJ 3531 SDOI2014 旅行 树链剖分

题目大意:给定一棵树,每一个点有一个权值和一个颜色.多次改变一些点的权值和颜色,多次求一条路径上与起点和终点颜色同样的点的权值和以及权值最大值 每种颜色开一个线段树 动态开节点 每一个点仅仅建一条链 这样空间复杂度是O(nlogn)的 然后就正常树链剖分即可了 #include <queue> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm>

【BZOJ 3531】【SDOI 2014】旅行 树链剖分

因为有$10^5$个宗教,需要开$10^5$个线段树. 平时开的线段树是“满”二叉树,但在这个题中代表一个宗教的线段树管辖的区间有很多点都不属于这个宗教,也就不用“把枝叶伸到这个点上”,所以这样用类似主席树的数组动态开点来建立$10^5$个只有几个“树枝”的线段树,维护轻重链就可以了 线段树的$L,R,l,r$弄反了调了好久$QAQ$ $so$ $sad$ #include<cstdio> #include<cstring> #include<algorithm> #d

BZOJ3531 SDOI2014 旅行 - 树链剖分,主席树

题意:给定一棵树,树上每个点有权值和类型.支持:修改某个点的类型:修改某个点的权值:询问某条链上某个类型的点的和/最大值.点数/类型数/询问数<=100000. 分析: 树链剖分,对每个类型的点建立线段树(动态开点). note: 忘记写t_query返回值调半天-- 莫名其妙地1A 代码: 1 #include <bits/stdc++.h> 2 using namespace std; 3 4 int a[5000005],s[5000005],dep[100005],size[10

【Luogu P3384】树链剖分模板

树链剖分的基本思想是把一棵树剖分成若干条链,再利用线段树等数据结构维护相关数据,可以非常暴力优雅地解决很多问题. 树链剖分中的几个基本概念: 重儿子:对于当前节点的所有儿子中,子树大小最大的一个儿子就是重儿子(子树大小相同的则随意取一个) 轻儿子:不是重儿子就是轻儿子 重边:连接父节点和重儿子的边 轻边:连接父节点和轻儿子的边 重链:相邻重边相连形成的链 值得注意的还有以下几点: 叶子节点没有重儿子也没有轻儿子: 对于每一条重链,其起点必然是轻儿子: 单独一个轻叶子节点也是一条重链: 结合上面三

BZOJ.3531.旅行(树链剖分 动态开点)

题目链接 无优化版本(170行): /* 首先树剖可以维护树上的链Sum.Max 可以对每个宗教建一棵线段树,那这题就很好做了 不过10^5需要动态开点 (不明白为什么nlogn不需要回收就可以 不是每个Insert加log个节点?) 操作修改完更改原数列!盲人..少玩rts.. */ #include<cstdio> #include<cctype> #include<algorithm> #define gc() getchar() #define now node

【BZOJ3531】【Sdoi2014】旅行 树链剖分。

广告: #include <stdio.h> int main() { puts("转载请注明出处[vmurder]谢谢"); puts("网址:blog.csdn.net/vmurder/article/details/44026729"); } 题解: 开10W棵线段树,然后节点动态加. 然后对于单独线段树树剖. 天哪!!CFree竟然吞了我一个'&'符号. 恶心死了找了正经好一会. 代码: #include <cstdio> #

luoguP3384 [模板]树链剖分

luogu P3384 [模板]树链剖分 题目 #include<iostream> #include<cstdlib> #include<cstdio> #include<cmath> #include<cstring> #include<iomanip> #include<algorithm> #include<ctime> #include<queue> #define rg register