LUOGU P1505 [国家集训队]旅游 (树链剖分+线段树)

传送门

解题思路

快被调死的码农题,,,其实就是一个边权下放到点权的线段树+树剖。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>

using namespace std;
const int MAXN = 100005;
const int inf = 0x3f3f3f3f;

inline int rd(){
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {f=ch==‘-‘?0:1;ch=getchar();}
    while(isdigit(ch))  {x=(x<<1)+(x<<3)+ch-‘0‘;ch=getchar();}
    return f?x:-x;
}

int n,m,head[MAXN],cnt=1,to[MAXN<<1],nxt[MAXN<<1],val[MAXN<<1],num;
int w[MAXN],wt[MAXN],id[MAXN],top[MAXN],siz[MAXN],son[MAXN],fa[MAXN],dep[MAXN];
int Sum[MAXN<<2],Min[MAXN<<2],Max[MAXN<<2];
bool rev[MAXN<<2],lazy[MAXN<<2];

inline void add(int bg,int ed,int ww){
    to[++cnt]=ed,nxt[cnt]=head[bg],head[bg]=cnt,val[cnt]=ww;
}

void dfs1(int x,int f,int d){
    dep[x]=d,fa[x]=f,siz[x]=1;int maxson=-1,u;
    for(register int i=head[x];i;i=nxt[i]){
        u=to[i];if(u==f) continue;
        dfs1(u,x,d+1);siz[x]+=siz[u];w[u]=val[i];
        if(siz[u]>maxson) {maxson=siz[u];son[x]=u;}
    }
}

void dfs2(int x,int topf){
    top[x]=topf;id[x]=++num;wt[num]=w[x];
    if(!son[x]) return;dfs2(son[x],topf);int u;
    for(register int i=head[x];i;i=nxt[i]){
        u=to[i];if(u==fa[x] || u==son[x]) continue;
        dfs2(u,u);
    }
}

inline void pushdown(int x){
    rev[x<<1]^=1;rev[x<<1|1]^=1;rev[x]^=1;
    Sum[x<<1]=-Sum[x<<1];Sum[x<<1|1]=-Sum[x<<1|1];
    swap(Min[x<<1],Max[x<<1]);swap(Min[x<<1|1],Max[x<<1|1]);
    Min[x<<1]=-Min[x<<1];Min[x<<1|1]=-Min[x<<1|1];
    Max[x<<1]=-Max[x<<1];Max[x<<1|1]=-Max[x<<1|1];
}

void build(int x,int l,int r){
    if(l==r) {if(l==1) Min[x]=inf,Max[x]=-inf; else Sum[x]=Min[x]=Max[x]=wt[l];return;}
    int mid=(l+r)>>1;
    build(x<<1,l,mid);build(x<<1|1,mid+1,r);
    Sum[x]=Sum[x<<1]+Sum[x<<1|1];
    Min[x]=min(Min[x<<1],Min[x<<1|1]);
    Max[x]=max(Max[x<<1],Max[x<<1|1]);
}

void update(int x,int l,int r,int L,int R,int ww){
    if(L<=l && r<=R) {
        if(ww==-inf) {rev[x]^=1;Sum[x]=-Sum[x];swap(Min[x],Max[x]);Min[x]=-Min[x];Max[x]=-Max[x];}
        else Sum[x]=Min[x]=Max[x]=ww;
        return;
    }
    int mid=(l+r)>>1;if(rev[x]) pushdown(x);
    if(L<=mid) update(x<<1,l,mid,L,R,ww);
    if(mid<R)  update(x<<1|1,mid+1,r,L,R,ww);
    Sum[x]=Sum[x<<1]+Sum[x<<1|1];
    Min[x]=min(Min[x<<1],Min[x<<1|1]);
    Max[x]=max(Max[x<<1],Max[x<<1|1]);
}

int query_Sum(int x,int l,int r,int L,int R){
    if(L<=l && r<=R) return Sum[x];
    int mid=(l+r)>>1,ret=0;if(rev[x]) pushdown(x);
    if(L<=mid) ret+=query_Sum(x<<1,l,mid,L,R);
    if(mid<R)  ret+=query_Sum(x<<1|1,mid+1,r,L,R);
    return ret;
}

int query_Max(int x,int l,int r,int L,int R){
    if(L<=l && r<=R) return Max[x];
    int mid=(l+r)>>1,ret=-inf;if(rev[x]) pushdown(x);
    if(L<=mid) ret=max(ret,query_Max(x<<1,l,mid,L,R));
    if(mid<R)  ret=max(ret,query_Max(x<<1|1,mid+1,r,L,R));
    return ret;
}

int query_Min(int x,int l,int r,int L,int R){
    if(L<=l && r<=R) return Min[x];
    int mid=(l+r)>>1,ret=inf;if(rev[x]) pushdown(x);
    if(L<=mid) ret=min(ret,query_Min(x<<1,l,mid,L,R));
    if(mid<R)  ret=min(ret,query_Min(x<<1|1,mid+1,r,L,R));
    return ret;
}

void updRev(int x,int y){
    while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]]) swap(x,y);
        update(1,1,n,id[top[x]],id[x],-inf);
        x=fa[top[x]];
    }
    if(dep[x]>dep[y]) swap(x,y);
    if(x!=y) update(1,1,n,id[x]+1,id[y],-inf);
}

inline int qSum(int x,int y){
    int ret=0;
    while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]]) swap(x,y);
        ret+=query_Sum(1,1,n,id[top[x]],id[x]);
        x=fa[top[x]];
    }
    if(dep[x]>dep[y]) swap(x,y);
    if(x!=y) ret+=query_Sum(1,1,n,id[x]+1,id[y]);
    return ret;
}

inline int qMax(int x,int y){
    int ret=-inf;
    while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]]) swap(x,y);
        ret=max(ret,query_Max(1,1,n,id[top[x]],id[x]));
        x=fa[top[x]];
    }
    if(dep[x]>dep[y]) swap(x,y);
    if(x!=y) ret=max(ret,query_Max(1,1,n,id[x]+1,id[y]));
    return ret;
}

inline int qMin(int x,int y){
    int ret=inf;
    while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]]) swap(x,y);
        ret=min(ret,query_Min(1,1,n,id[top[x]],id[x]));
        x=fa[top[x]];
    }
    if(dep[x]>dep[y]) swap(x,y);if(x!=y) ret=min(ret,query_Min(1,1,n,id[x]+1,id[y]));
    return ret;
}

inline int pre(int x){
    return dep[to[x<<1]]>dep[to[x<<1^1]]?to[x<<1]:to[x<<1^1];
}

int main(){
//    freopen("wrong.out","w",stdout);
    n=rd();int x,y,z;char s[5];
    for(int i=1;i<n;i++){
        x=rd()+1,y=rd()+1,z=rd();
        add(x,y,z),add(y,x,z);
    }
    dfs1(1,0,1);dfs2(1,1);build(1,1,n);m=rd();
    while(m--){
        scanf("%s",s+1);x=rd(),y=rd();
        if(s[1]==‘C‘) update(1,1,n,id[pre(x)],id[pre(x)],y);
        else if(s[1]==‘N‘) updRev(x+1,y+1);
        else if(s[1]==‘S‘) printf("%d\n",qSum(x+1,y+1));
        else if(s[2]==‘A‘) printf("%d\n",qMax(x+1,y+1));
        else printf("%d\n",qMin(x+1,y+1));
    }
    return 0;
}

原文地址:https://www.cnblogs.com/sdfzsyq/p/9734620.html

时间: 2024-10-01 23:55:31

LUOGU P1505 [国家集训队]旅游 (树链剖分+线段树)的相关文章

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

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

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

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

【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条探险航