BZOJ 2157 旅游(树链剖分+线段树)

【题目链接】 http://www.lydsy.com/JudgeOnline/problem.php?id=2157

【题目大意】

  支持修改边,链上查询最大值最小值总和,以及链上求相反数

【题解】

  树链剖分,然后线段树维护线段操作即可。

【代码】

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int INF=~0U>>1;
const int N=20010,M=N<<2;
int a[N];
namespace Segment_Tree{
    int tot;
    struct node{int l,r,a,b,rev_tag,min_val,max_val,sum;}T[M];
    void build(int,int);
    void Initialize(int n){
        tot=0;
        build(1,n);
    }
    void addtag(int x){
        T[x].sum=-T[x].sum;
        T[x].max_val=-T[x].max_val;
        T[x].min_val=-T[x].min_val;
        swap(T[x].min_val,T[x].max_val);
        T[x].rev_tag^=1;
    }
    void pb(int x){
        if(T[x].rev_tag){
            if(T[x].l)addtag(T[x].l);
            if(T[x].r)addtag(T[x].r);
            T[x].rev_tag^=1;
        }
    }
    void up(int x){
        T[x].sum=T[T[x].l].sum+T[T[x].r].sum;
        T[x].max_val=max(T[T[x].l].max_val,T[T[x].r].max_val);
        T[x].min_val=min(T[T[x].l].min_val,T[T[x].r].min_val);
    }
    void build(int l,int r){
        int x=++tot;
        T[x].a=l;T[x].b=r;T[x].rev_tag=T[x].l=T[x].r=0;
        if(l==r){T[x].sum=T[x].min_val=T[x].max_val=a[l];return;}
        int mid=(l+r)>>1;
        T[x].l=tot+1;build(l,mid);
        T[x].r=tot+1;build(mid+1,r);
        up(x);
    }
    void change(int x,int pos,int p){
        if(T[x].a==T[x].b){T[x].sum=T[x].min_val=T[x].max_val=p;return;}
        if(T[x].rev_tag)pb(x);
        int mid=(T[x].a+T[x].b)>>1;
        if(mid>=pos&&T[x].l)change(T[x].l,pos,p);
        if(mid<pos&&T[x].r)change(T[x].r,pos,p);
        up(x);
    }
    void reverse(int x,int a,int b){
        if(a<=T[x].a&&T[x].b<=b){addtag(x);return;}
        if(T[x].rev_tag)pb(x); int mid=(T[x].a+T[x].b)>>1;
        if(a<=mid)reverse(T[x].l,a,b);
        if(b>mid)reverse(T[x].r,a,b);
        up(x);
    }
    int query_sum(int x,int a,int b){
        if(a<=T[x].a&&T[x].b<=b)return T[x].sum;
        if(T[x].rev_tag)pb(x); int mid=(T[x].a+T[x].b)>>1,res=0;
        if(a<=mid)res+=query_sum(T[x].l,a,b);
        if(b>mid)res+=query_sum(T[x].r,a,b);
        return res;
    }
    int query_min(int x,int a,int b){
    	//printf("%d %d %d\n",T[x].min_val,a,b);
        if(a<=T[x].a&&T[x].b<=b)return T[x].min_val;
        if(T[x].rev_tag)pb(x); int mid=(T[x].a+T[x].b)>>1,res=INF;
        if(a<=mid)res=min(res,query_min(T[x].l,a,b));
        if(b>mid)res=min(res,query_min(T[x].r,a,b));
        return res;
    }
    int query_max(int x,int a,int b){
        if(a<=T[x].a&&T[x].b<=b)return T[x].max_val;
        if(T[x].rev_tag)pb(x); int mid=(T[x].a+T[x].b)>>1,res=-INF;
        if(a<=mid)res=max(res,query_max(T[x].l,a,b));
        if(b>mid)res=max(res,query_max(T[x].r,a,b));
        return res;
    }
}
namespace Tree_Chain_Subdivision{
    int ed,root,d[N],v[N<<1],vis[N],f[N],g[N<<1];
    int nxt[N<<1],size[N],son[N],st[N],en[N],dfn,top[N];
    void add_edge(int x,int y){v[++ed]=y;nxt[ed]=g[x];g[x]=ed;}
    void dfs(int x){
        size[x]=1;
        for(int i=g[x];i;i=nxt[i])if(v[i]!=f[x]){
            f[v[i]]=x,d[v[i]]=d[x]+1;
            dfs(v[i]),size[x]+=size[v[i]];
            if(size[v[i]]>size[son[x]])son[x]=v[i];
        }
    }
    void dfs2(int x,int y){
        if(x==-1)return;
        st[x]=++dfn;top[x]=y;
        if(son[x])dfs2(son[x],y);
        for(int i=g[x];i;i=nxt[i])if(v[i]!=son[x]&&v[i]!=f[x])dfs2(v[i],v[i]);
        en[x]=dfn;
    }
    // 查询x,y两点的lca
    int lca(int x,int y){
        for(;top[x]!=top[y];x=f[top[x]])if(d[top[x]]<d[top[y]]){int z=x;x=y;y=z;}
        return d[x]<d[y]?x:y;
    }
    // x是y的祖先,查询x到y方向的第一个点
    int lca2(int x,int y){
        int t;
        while(top[x]!=top[y])t=top[y],y=f[top[y]];
        return x==y?t:son[x];
    }
    // 对x到y路径上的点取反操作
    void reverse(int x,int y){
        for(;top[x]!=top[y];x=f[top[x]]){
            if(d[top[x]]<d[top[y]]){int z=x;x=y;y=z;}
            Segment_Tree::reverse(1,st[top[x]],st[x]);
        }if(d[x]<d[y]){int z=x;x=y;y=z;}
        Segment_Tree::reverse(1,st[y]+1,st[x]);
    }
    // 查询x到y路径上的最小值
    int query_min(int x,int y){
        int res=INF;
        for(;top[x]!=top[y];x=f[top[x]]){
            if(d[top[x]]<d[top[y]]){int z=x;x=y;y=z;}
            res=min(res,Segment_Tree::query_min(1,st[top[x]],st[x]));
        }if(d[x]<d[y]){int z=x;x=y;y=z;}
        res=min(res,Segment_Tree::query_min(1,st[y]+1,st[x]));
        return res;
    }
    // 查询x到y路径上的最大值
    int query_max(int x,int y){
        int res=-INF;
        for(;top[x]!=top[y];x=f[top[x]]){
            if(d[top[x]]<d[top[y]]){int z=x;x=y;y=z;}
            res=max(res,Segment_Tree::query_max(1,st[top[x]],st[x]));
        }if(d[x]<d[y]){int z=x;x=y;y=z;}
        res=max(res,Segment_Tree::query_max(1,st[y]+1,st[x]));
        return res;
    }
    // 查询x到y路径上的总和
    int query_sum(int x,int y){
        int res=0;
        for(;top[x]!=top[y];x=f[top[x]]){
            if(d[top[x]]<d[top[y]]){int z=x;x=y;y=z;}
            res=res+Segment_Tree::query_sum(1,st[top[x]],st[x]);
        }if(d[x]<d[y]){int z=x;x=y;y=z;}
        res=res+Segment_Tree::query_sum(1,st[y]+1,st[x]);
        return res;
    }
    void Initialize(){
        memset(g,dfn=ed=0,sizeof(g));
        memset(v,0,sizeof(v));
        memset(nxt,0,sizeof(nxt));
        memset(son,-1,sizeof(son));
    }
}
int n,m,e[N][3];
char op[5];
int main(){
    scanf("%d",&n);
    using namespace Tree_Chain_Subdivision;
    Initialize();
    for(int i=0;i<n-1;i++){
        scanf("%d%d%d",&e[i][0],&e[i][1],&e[i][2]);
        e[i][0]++; e[i][1]++;
        add_edge(e[i][0],e[i][1]);
        add_edge(e[i][1],e[i][0]);
    }dfs(1);dfs2(1,1);
    for(int i=0;i<n-1;i++){
        if(d[e[i][0]]>d[e[i][1]])swap(e[i][0],e[i][1]);
        a[st[e[i][1]]]=e[i][2];
    }
	Segment_Tree::Initialize(n);
    scanf("%d",&m);
    while(m--){
        scanf("%s",op);
        if(op[0]==‘C‘){
            int x,y;
            scanf("%d%d",&x,&y);
            Segment_Tree::change(1,st[e[x-1][1]],y);
        }
        else if(op[0]==‘N‘){
            int x,y;
            scanf("%d%d",&x,&y);
            Tree_Chain_Subdivision::reverse(x+1,y+1);
        }
        else if(op[0]==‘S‘){
            int x,y;
            scanf("%d%d",&x,&y);
            printf("%d\n",Tree_Chain_Subdivision::query_sum(x+1,y+1));
        }
        else if(op[1]==‘I‘){
            int x,y;
            scanf("%d%d",&x,&y);
            printf("%d\n",Tree_Chain_Subdivision::query_min(x+1,y+1));
        }
        else{
            int x,y;
            scanf("%d%d",&x,&y);
            printf("%d\n",Tree_Chain_Subdivision::query_max(x+1,y+1));
        }
    }return 0;
}
时间: 2024-11-03 22:16:05

BZOJ 2157 旅游(树链剖分+线段树)的相关文章

bzoj 2157: 旅游【树链剖分+线段树】

裸的树链剖分+线段树 但是要注意一个地方--我WA了好几次才发现取完相反数之后max值和min值是要交换的-- #include<iostream> #include<cstdio> using namespace std; const int N=200005; int n,m,h[N],cnt,de[N],va[N],fa[N],si[N],hs[N],fr[N],id[N],tot,rl[N]; char c[10]; struct qwe { int ne,no,to,va

【BZOJ】1146: [CTSC2008]网络管理Network(树链剖分+线段树套平衡树+二分 / dfs序+树状数组+主席树)

第一种做法(时间太感人): 这题我真的逗了,调了一下午,疯狂造数据,始终找不到错. 后来发现自己sb了,更新那里没有打id,直接套上u了.我.... 调了一下午啊!一下午的时光啊!本来说好中午A掉去学习第二种做法,噗 好吧,现在第一种做法是hld+seg+bst+二分,常数巨大,log^4级别,目前只会这种. 树剖后仍然用线段树维护dfs序区间,然后在每个区间建一颗平衡树,我用treap,(这题找最大啊,,,囧,并且要注意,这里的rank是比他大的数量,so,我们在二分时判断要判断一个范围,即要

Aizu 2450 Do use segment tree 树链剖分+线段树

Do use segment tree Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://www.bnuoj.com/v3/problem_show.php?pid=39566 Description Given a tree with n (1 ≤ n ≤ 200,000) nodes and a list of q (1 ≤ q ≤ 100,000) queries, process the queries in order and out

Hdu 3966 Aragorn&#39;s Story (树链剖分 + 线段树区间更新)

题目链接: Hdu 3966 Aragorn's Story 题目描述: 给出一个树,每个节点都有一个权值,有三种操作: 1:( I, i, j, x ) 从i到j的路径上经过的节点全部都加上x: 2:( D, i, j, x ) 从i到j的路径上经过的节点全部都减去x: 3:(Q, x) 查询节点x的权值为多少? 解题思路: 可以用树链剖分对节点进行hash,然后用线段树维护(修改,查询),数据范围比较大,要对线段树进行区间更新 1 #include <cstdio> 2 #include

【bzoj3589】动态树 树链剖分+线段树

题目描述 别忘了这是一棵动态树, 每时每刻都是动态的. 小明要求你在这棵树上维护两种事件 事件0:这棵树长出了一些果子, 即某个子树中的每个节点都会长出K个果子. 事件1:小明希望你求出几条树枝上的果子数. 一条树枝其实就是一个从某个节点到根的路径的一段. 每次小明会选定一些树枝, 让你求出在这些树枝上的节点的果子数的和. 注意, 树枝之间可能会重合, 这时重合的部分的节点的果子只要算一次. 输入 第一行一个整数n(1<=n<=200,000), 即节点数. 接下来n-1行, 每行两个数字u,

BZOJ2243 (树链剖分+线段树)

Problem 染色(BZOJ2243) 题目大意 给定一颗树,每个节点上有一种颜色. 要求支持两种操作: 操作1:将a->b上所有点染成一种颜色. 操作2:询问a->b上的颜色段数量. 解题分析 树链剖分+线段树. 开一个记录类型,记录某一段区间的信息.l 表示区间最左侧的颜色 , r 表示区间最右侧的颜色 , sum 表示区间中颜色段数量. 合并时判断一下左区间的右端点和有区间的左端点的颜色是否一样. 树上合并时需要用两个变量ans1,ans2来存储.ans1表示x往上走时形成的链的信息,

bzoj4304 (树链剖分+线段树)

Problem T2 (bzoj4304 HAOI2015) 题目大意 给定一颗树,1为根节点,要求支持三种操作. 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a . 操作 3 :询问某个节点 x 到根的路径中所有点的点权和. 解题分析 练手题.树链剖分+线段树. 参考程序 1 #include <cstdio> 2 #include <cstring> 3 #include <cmath> 4 #incl

【bzoj4811】[Ynoi2017]由乃的OJ 树链剖分+线段树区间合并

题目描述 由乃正在做她的OJ.现在她在处理OJ上的用户排名问题.OJ上注册了n个用户,编号为1-",一开始他们按照编号 排名.由乃会按照心情对这些用户做以下四种操作,修改用户的排名和编号:然而由乃心情非常不好,因为Deus天 天问她题...因为Deus天天问由乃OI题,所以由乃去学习了一下OI,由于由乃智商挺高,所以OI学的特别熟练她 在RBOI2016中以第一名的成绩进入省队,参加了NOI2016获得了金牌保送 Deus:这个题怎么做呀? yuno:这个不是NOI2014的水题吗... Deu

HDU 2460 Network(双连通+树链剖分+线段树)

HDU 2460 Network 题目链接 题意:给定一个无向图,问每次增加一条边,问个图中还剩多少桥 思路:先双连通缩点,然后形成一棵树,每次增加一条边,相当于询问这两点路径上有多少条边,这个用树链剖分+线段树处理 代码: #include <cstdio> #include <cstring> #include <algorithm> #include <vector> using namespace std; #pragma comment(linke

【bzoj1959】[Ahoi2005]LANE 航线规划 离线处理+树链剖分+线段树

题目描述 对Samuel星球的探险已经取得了非常巨大的成就,于是科学家们将目光投向了Samuel星球所在的星系——一个巨大的由千百万星球构成的Samuel星系. 星际空间站的Samuel II巨型计算机经过长期探测,已经锁定了Samuel星系中许多星球的空间坐标,并对这些星球从1开始编号1.2.3……. 一些先遣飞船已经出发,在星球之间开辟探险航线. 探险航线是双向的,例如从1号星球到3号星球开辟探险航线,那么从3号星球到1号星球也可以使用这条航线. 例如下图所示: 在5个星球之间,有5条探险航