<题目链接>

题目大意:

给定一棵无向树,这棵树的有边权,这棵树的边的序号完全由输入边的序号决定。给你一个人的起点,进行两次操作:

一:该人从起点走到指定点,问你这段路径的边权总和是多少。

二:对指定序号的边的权值做一些改变。

解题分析:

本题用的是树链剖分,同时用线段树去维护剖分出的树链。并且,本题也是无向边权转点权的典型例题,这部分要重点掌握。

#include <cstdio>
#include <cstring>
#define Lson l,mid,rt<<1
#define Rson mid+1,r,rt<<1|1
using namespace std;
const int M = 1e5+;
typedef long long ll;
int cnt,tot,head[M],p[M];
int n,q,pos,pp;
int sz[M],top[M],son[M],id[M],rnk[M],f[M],dep[M]; ll a[M]; //代表该点的权值
struct EDGE{
int v,next;
ll w;
}edge[M<<];
struct Tree
{
ll sum;
}tree[M<<];
void init(){
tot=cnt=;
memset(head,-,sizeof(head));
}
void add(int u,int v,ll w){
edge[++cnt].v=v,edge[cnt].w=w,edge[cnt].next=head[u];
head[u]=cnt;
}
void fsd(int u,int fa){ //将边权转化为点权
for(int i=head[u];~i;i=edge[i].next){
int v=edge[i].v;ll w=edge[i].w;
if(v==fa) continue;
a[v]=w;
p[(i-)/+]=v; //(i-1)/2+1 为该边加入的实际序号,因为加入某条边的时候,是将该边正的存一遍,反向存一遍,同一条边在e[]数组中是临近的,所以这里可以这样求该边加入的序号
//这里感觉挺巧妙的,p[i]表示序号为i的边对应的点(该点点权表示该边边权)
fsd(v,u); //继续向下延伸,将边权转化为点权
}
return;
}
void dfs(int u,int fa,int d){ //将重儿子标记,以及一系列的预处理
sz[u]=;f[u]=fa;son[u]=-;dep[u]=d;
for(int i=head[u];~i;i=edge[i].next){
int v=edge[i].v;
if(v==fa) continue;
dfs(v,u,d+);
sz[u]+=sz[v];
if(son[u]==-||sz[v]>sz[son[u]]) son[u]=v;
}
return ;
}
void dfs1(int u,int t){ //将重链全部找出来
id[u]=++tot;
rnk[tot]=u;
top[u]=t;
if(son[u]==-) return ;
dfs1(son[u],t);
for(int i=head[u];~i;i=edge[i].next){
int v=edge[i].v;
if(v==f[u]||v==son[u]) continue;
dfs1(v,v);
}
return ;
}
void Pushup(int rt){
tree[rt].sum=tree[rt<<].sum+tree[rt<<|].sum;
}
void build(int l,int r,int rt){
tree[rt].sum=;
if(l==r){
tree[rt].sum=a[rnk[l]];
return ;
}
int mid=(l+r)>>;
build(Lson);
build(Rson);
Pushup(rt);
}
void update(int l,int r,int rt,int loc,ll v){ //单点更新
if(l==r){
tree[rt].sum=v;
return ;
}
int mid=(l+r)>>;
if(loc<=mid) update(Lson,loc,v);
else update(Rson,loc,v);
Pushup(rt);
}
ll query(int L,int R,int l,int r,int rt){
if(L<=l&&r<=R){
return tree[rt].sum;
}
ll ans=;
int mid=(l+r)>>;
if(L<=mid) ans+=query(L,R,Lson);
if(R>mid) ans+=query(L,R,Rson);
return ans;
}
/*--树上路径边权总和查询 --*/
ll sum(int x,int y){
int fx=top[x],fy=top[y];ll res=;
while(fx!=fy){ //如果这两个点不在一条重链上则一直向上跳,并且不断更新
if(dep[fx]>dep[fy]){
res+=query(id[fx],id[x],,n,); //因为在线段树中,fx的编号一定比x编号小
x=f[fx],fx=top[x]; //从这条重链爬到父节点所在重链的链首上去
}
else{
res+=query(id[fy],id[y],,n,);
y=f[fy],fy=top[y];
}
}
if(x==y) return res; //!!!,当x==y的时候要特判一下
if(dep[x]<dep[y])
res+=query(id[son[x]],id[y],,n,); //!!!,这里要注意,因为本题将边权转化为点权,所以计算x-->y的边权值,相当于直接计算son[x]-->y的点权值总和
else
res+=query(id[son[y]],id[x],,n,);
return res;
}
int main(){
init();
scanf("%d%d%d",&n,&q,&pos);
for(int i=;i<n;i++){
int u,v;ll w;
scanf("%d%d%lld",&u,&v,&w);
add(u,v,w);add(v,u,w);
}
/*-- 将边权转化为点权 --*/
fsd(,-); //从标号为1的点开始将边权转化为点权,其实这里从哪个点为起点都无所谓
a[]=; //因为是以1为起点将边权转化为点权,所以1没有权值,这里将1的权值赋0
/*--树链剖分 --*/
dfs(,-,);
dfs1(,); build(,n,); //将剖分出的树链用线段树维护
while(q--){
int op;
scanf("%d",&op);
if(op==){ //从当前点走到指定点的路径权值总和
int to;
scanf("%d",&to);
printf("%lld\n",sum(pos,to));
pos=to; //更新该人的起点
}
else{
int cal;ll w;
scanf("%d%lld",&cal,&w); //cal为需要更新的边的序号
update(,n,,id[p[cal]],w);
}
}
return ;
}

2018-09-10

POJ 2763 Housewife Wind 【树链剖分】+【线段树】的更多相关文章

  1. POJ.2763 Housewife Wind ( 边权树链剖分 线段树维护区间和 )

    POJ.2763 Housewife Wind ( 边权树链剖分 线段树维护区间和 ) 题意分析 给出n个点,m个询问,和当前位置pos. 先给出n-1条边,u->v以及边权w. 然后有m个询问 ...

  2. 【BZOJ-2325】道馆之战 树链剖分 + 线段树

    2325: [ZJOI2011]道馆之战 Time Limit: 40 Sec  Memory Limit: 256 MBSubmit: 1153  Solved: 421[Submit][Statu ...

  3. 【BZOJ2243】[SDOI2011]染色 树链剖分+线段树

    [BZOJ2243][SDOI2011]染色 Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的 ...

  4. BZOJ2243 (树链剖分+线段树)

    Problem 染色(BZOJ2243) 题目大意 给定一颗树,每个节点上有一种颜色. 要求支持两种操作: 操作1:将a->b上所有点染成一种颜色. 操作2:询问a->b上的颜色段数量. ...

  5. POJ3237 (树链剖分+线段树)

    Problem Tree (POJ3237) 题目大意 给定一颗树,有边权. 要求支持三种操作: 操作一:更改某条边的权值. 操作二:将某条路径上的边权取反. 操作三:询问某条路径上的最大权值. 解题 ...

  6. bzoj4034 (树链剖分+线段树)

    Problem T2 (bzoj4034 HAOI2015) 题目大意 给定一颗树,1为根节点,要求支持三种操作. 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把某个节点 x 为根的子 ...

  7. HDU4897 (树链剖分+线段树)

    Problem Little Devil I (HDU4897) 题目大意 给定一棵树,每条边的颜色为黑或白,起始时均为白. 支持3种操作: 操作1:将a->b的路径中的所有边的颜色翻转. 操作 ...

  8. 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 ...

  9. 【POJ3237】Tree(树链剖分+线段树)

    Description You are given a tree with N nodes. The tree’s nodes are numbered 1 through N and its edg ...

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

    HDU 2460 Network 题目链接 题意:给定一个无向图,问每次增加一条边,问个图中还剩多少桥 思路:先双连通缩点,然后形成一棵树,每次增加一条边,相当于询问这两点路径上有多少条边,这个用树链 ...

随机推荐

  1. Windows7上完全卸载Oracle 12c操作步骤

    注:本文来源于:< Windows7上完全卸载Oracle  12c操作步骤 > 1.关闭Oracle所有的服务,按[win+R]运行[services.msc]找到所有Oracle开头的 ...

  2. 疯狂Workflow讲义——基于Activiti的工作流应用开 PDF 下载

    <疯狂Workflow讲义--基于Activiti的工作流应用开> 一:文档获取下载方式: 1:花20CSDN积分:可以下载:http://download.csdn.net/downlo ...

  3. Confluence 6 用户提交的备份和恢复脚本

    下面的代码是用户提交的,在使用的时候需要小心,因为 Atlassian 不提供这些代码的技术支持.如果你在使用或者修改这些代码的时候有任何问题,请粘贴到 post them to Atlassian ...

  4. 关于vue的基础概念

    vue-cli相当于脚手架 给你自动生成模板工程vue-router是 vue路由插件 支持你单页应用的vue-loader是webpack下loader插件 可以把.vue文件 输出成组件

  5. Little Red Riding Hood

    问题 : Little Red Riding Hood 时间限制: 1 Sec  内存限制: 1280 MB 题目描述 Once upon a time, there was a little gir ...

  6. Python随手记—各种方法的使用

    os.popen()方法的使用 os.popen()方法用于从一个命令打开一个管道. 语法:os.popen(command[, mode[, bufsize]])    其中 command是使用的 ...

  7. python调用PHP方法

    PHP代码如下:<?php $method = $argv[1]; $param1 = $argv[2]; $param2 = $argv[3]; if(isset($method) & ...

  8. C++ Primer 笔记——基本内置类型

    1.算术类型分为两类:整型和浮点型.算术类型的尺寸在不同机器上有所差别,下表列出了C++标准规定的尺寸的最小值.同时允许编译器赋予这些类型更大的尺寸. 一个char的大小和一个机器字节一样. 一个in ...

  9. js 打开摄像头方法 (定制摄像头)

    var video = document.getElementById("video");if (navigator.mediaDevices && navigat ...

  10. js中匿名函数和回调函数

    匿名函数: 通过这种方式定义的函数:(没有名字的函数) 作用:当它不被赋值给变量单独使用的时候 1.将匿名函数作为参数传递给其他函数 2.定义某个匿名函数来执行某些一次性任务 var f = func ...