BZOJ 2157 旅游(动态树)

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

【题目大意】

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

【题解】

  我们将边转化成点,直接用LCT可以处理以上操作

【代码】

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int N=200010;
int n;
const int INF=~0U>>1;
namespace Link_Cut_Tree{
    int f[N],son[N][2],tmp[N],tag[N],val[N],sum[N],mx[N],mn[N];
    bool rev[N];
    void Initialize(){
        memset(f,0,sizeof(f));
        memset(son,0,sizeof(son));
        memset(val,0,sizeof(val));
        memset(rev,0,sizeof(rev));
        memset(sum,0,sizeof(sum));
        memset(tag,0,sizeof(tag));
    }
    void reverse(int x){
        sum[x]=-sum[x];
        val[x]=-val[x];
        swap(mn[x],mx[x]);
        mn[x]=-mn[x],mx[x]=-mx[x];
        tag[x]^=1;
    }
    bool isroot(int x){return !f[x]||son[f[x]][0]!=x&&son[f[x]][1]!=x;}
    void rev1(int x){if(!x)return;swap(son[x][0],son[x][1]);rev[x]^=1;}
    void pb(int x){
        if(rev[x])rev1(son[x][0]),rev1(son[x][1]),rev[x]=0;
        if(tag[x]){
            tag[x]=0;
            if(son[x][0])reverse(son[x][0]);
            if(son[x][1])reverse(son[x][1]);
        }
    }
    void up(int x){
        sum[x]=val[x]+sum[son[x][0]]+sum[son[x][1]];
        mx[x]=max(mx[son[x][0]],mx[son[x][1]]);
        mn[x]=min(mn[son[x][0]],mn[son[x][1]]);
        if(x>n)mx[x]=max(mx[x],val[x]);
        if(x>n)mn[x]=min(mn[x],val[x]);
    }
    void rotate(int x){
        int y=f[x],w=son[y][1]==x;
        son[y][w]=son[x][w^1];
        if(son[x][w^1])f[son[x][w^1]]=y;
        if(f[y]){
            int z=f[y];
            if(son[z][0]==y)son[z][0]=x;else if(son[z][1]==y)son[z][1]=x;
        }f[x]=f[y];f[y]=x;son[x][w^1]=y;up(y);
    }
    void splay(int x){
        int s=1,i=x,y;tmp[1]=i;
        while(!isroot(i))tmp[++s]=i=f[i];
        while(s)pb(tmp[s--]);
        while(!isroot(x)){
            y=f[x];
            if(!isroot(y)){if((son[f[y]][0]==y)^(son[y][0]==x))rotate(x);else rotate(y);}
            rotate(x);
        }up(x);
    }
    void access(int x){for(int y=0;x;y=x,x=f[x])splay(x),son[x][1]=y,up(x);}
    // 查询x所在的树的根
    int root(int x){access(x);splay(x);while(son[x][0])x=son[x][0];return x;}
    // 使x成为根
    void makeroot(int x){access(x);splay(x);rev1(x);}
    // 将x和y所属树合并
    void link(int x,int y){makeroot(x);f[x]=y;access(x);}
    // 提取链
    void split(int x,int y){makeroot(y);access(x);splay(x);}
    // 查询x到y的链和
    int ask(int x,int y){split(x,y);return sum[x];}
    // 查询x到y的链最大值
    int askmx(int x,int y){split(x,y);return mx[x];}
    // 查询x到y的链最小值
    int askmn(int x,int y){split(x,y);return mn[x];}
}
int E[N],m;
int main(){
    scanf("%d",&n);
    using namespace Link_Cut_Tree;
    Initialize();
    for(int i=0;i<=n;i++)mx[i]=-INF,mn[i]=INF;
    int id=n;
    for(int i=1;i<n;i++){
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        E[i]=++id;
        link(x+1,id); link(y+1,id);
        val[id]=sum[id]=mx[id]=mn[id]=z;
    }char op[10];
    scanf("%d",&m);
    while(m--){
        scanf("%s",op);
        int x,y;
        scanf("%d%d",&x,&y);
        if(op[0]==‘N‘){split(x+1,y+1);reverse(x+1);}
        else if(op[0]==‘S‘){printf("%d\n",ask(x+1,y+1));}
        else if(op[1]==‘I‘){printf("%d\n",askmn(x+1,y+1));}
        else if(op[1]==‘A‘){printf("%d\n",askmx(x+1,y+1));}
        else{makeroot(E[x]);val[E[x]]=y;}
    }return 0;
}
时间: 2024-10-12 13:56:20

BZOJ 2157 旅游(动态树)的相关文章

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

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 2631: tree 动态树+常数优化

2631: tree Time Limit: 30 Sec  Memory Limit: 128 MBSubmit: 1716  Solved: 576[Submit][Status] Description 一棵n个点的树,每个点的初始权值为1.对于这棵树有q个操作,每个操作为以下四种操作之一:+ u v c:将u到v的路径上的点的权值都加上自然数c:- u1 v1 u2 v2:将树中原有的边(u1,v1)删除,加入一条新边(u2,v2),保证操作完之后仍然是一棵树:* u v c:将u到v的

BZOJ 2631 tree 动态树(Link-Cut-Tree)

题目大意:维护一种树形数据结构.支持下面操作: 1.树上两点之间的点权值+k. 2.删除一条边.添加一条边,保证加边之后还是一棵树. 3.树上两点之间点权值*k. 4.询问树上两点时间点的权值和. 思路:利用动态树维护这棵树,lct的裸题.假设不会下传标记的,先去做BZOJ1798,也是这种标记,仅仅只是在线段树上做,比这个要简单很多. 这个也是我的LCT的第一题,理解起来十分困难啊... CODE: #include <cstdio> #include <cstring> #in

bzoj 2759一个动态树好题

真的是动态树好题,如果把每个点的父亲设成p[x],那么建出来图应该是一个环套树森林,拆掉一条边,就变成了动态树,考虑维护什么,对于LCT上每个节点,维护两组k和b,一组是他到他父亲的,一组是他LCT子树中深度最深的点到深度最浅的点的父亲的k和b,查询时只需查询一颗树中sf到自己的k和b,判断是否有唯一解,然后再解就可以了.注意不能换根,因为树的形态是固定的. 1 #include<cstdio> 2 #include<cstring> 3 #include<iostream&

BZOJ 2157 旅游 树链剖分

题目大意:给出一棵树,支持以下操作:1.改变一条边的边权.2.将x到y路径的权值取反.3.查询x到y路径上最大值,最小值和权值和. 思路:好裸的链剖水题啊,唯一麻烦一点地是权值放在了边上,注意一下处理就没问题了.. CODE: #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define MAX 40010 #define INF 0x3f3f3f3

bzoj 2157 旅游

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2157 明明是裸LCT板子-- 注意pshp的那个地方!原以为把0~n的mx.mn都赋好初值就能随便弄了,仔细想想要用的是val! 而且还是既用在max里又用在min里,故不能赋初值来怎样,应当分类讨论! 关于变成相反数的操作,原来也是打标记!而且标记的含义和线段树的一样,表示自己已经操作过.要给孩子操作.这样才能保证query的正确. 可是自己那种dfs来当即弄成相反数的做法为什么不行??

BZOJ 2759 一个动态树好题 Link-Cut-Tree+扩展欧几里得

题目大意:给定n个形如xi=ki*x_pi+bi mod p的同余方程组 支持修改操作和求解操作 确实好题 感谢此题作者 顺便吐槽一下作者的Splay不加空节点太蛋疼了0.0 将每个点i的父亲设为pi 我们将会得到一座基环树林 将环上的一条边拆掉,在边的起始节点新开个域special_father记录这条边(P.S:好浪费 但是没办法) 于是我们得到了一座森林 显然可以用LCT来维护 每个节点的权值是个二元组(k,b),记录每个点关于答案的线性关系,合并时左侧代入右侧中 查询时将root的spe

主席树初探 &amp; bzoj 3295: [Cqoi2011] 动态逆序对 题解

[原题] 3295: [Cqoi2011]动态逆序对 Time Limit: 10 Sec  Memory Limit: 128 MB Submit: 778  Solved: 263 [Submit][Status] Description 对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数.给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数. Input 输入第一行包含两个整数n和m,即初始元素的个数和删除的元