传送门

题意:

  给出一棵树,每条边都有权值;

  给出 m 次询问,每次询问有三个参数 u,v,w ,求节点 u 与节点 v 之间权值 ≤ w 的路径个数;

题解:

  昨天再打比赛的时候,中途,凯少和我说,这道题,一眼看去,就是树链剖分,然鹅,太久没写树链剖分的我一时也木有思路;

  今天上午把树链剖分温习了一遍,做了个模板题;

  下午再想了一下这道题,思路喷涌而出............

  首先,介绍一下相关变量:

 int fa[maxn];//fa[u]:u的父节点
int son[maxn];//son[u]:u的重儿子
int dep[maxn];//dep[u]:u的深度
int siz[maxn];//siz[u]:以u为根的子树节点个数
int tid[maxn];//tid[u]:u在线段树中的位置
int top[maxn];//top[u]:u所在重链的祖先节点
int e[maxn][];//e[i][0]与e[i][1]有条权值为e[i][2]的边
vector<int >v[maxn<<];//v[i]:存储线段树中i号节点的所有边的权值

  (树链剖分,默认来看这篇博客的都会辽,逃)  

  下面重点介绍一下v[]的作用(将样例2中的权值改为了10):

  

  由树链剖分可知(图a,紫色部分代表重链)

    tid[1]=1,tid[3]=2,tid[5]=3;

    tid[2]=4,tid[4]=5;

  那么,线段树维护啥呢?

 struct SegmentTree
{
int l,r;
int mid()
{
return l+((r-l)>>);
}
}segTree[maxn<<];
vector<int >v[maxn<<];//v[i]:存储线段树中i号节点的所有边的权值

  对于我而言,此次线段树,主要维护节点 i 的左右区间[l,r],重点是 v[] 中维护的东西;

  首先将边权存到线段树中,如何存呢?

  对于边 u,v,w ,(假设 fa[v]=u),将 w 存在 v[ tid[ v ] ]中;

  看一下Update()函数:

 //将节点x在线段树中对应的pos位置的v中加入val
void Update(int x,int val,int pos)
{
if(segTree[pos].l == segTree[pos].r)
{
v[pos].push_back(val);//val加入到v[pos]中
return ;
}
int mid=segTree[pos].mid();
if(x <= mid)
Update(x,val,ls(pos));
else
Update(x,val,rs(pos));
}

  例如上图b:

  ①-② : 10 ,调用函数Update(tid[2],10,1) ⇔ v[tid[2]].push_back(10)

  ①-③ : 10 ,调用函数Update(tid[3],10,1) ⇔ v[tid[3]].push_back(10)

  ②-④ : 10 ,调用函数Update(tid[4],10,1) ⇔ v[tid[4]].push_back(10)

  ③-⑤ : 10 ,调用函数Update(tid[5],10,1) ⇔ v[tid[5]].push_back(10)

  线段树中的节点9中的v存储一个10

  线段树中的节点5中的v存储一个10

  线段树中的节点6中的v存储一个10

  线段树中的节点7中的v存储一个10

  这个就是Update()函数的作用;

  接下来的pushUp()函数很重要:

 void pushUp(int pos)
{
if(segTree[pos].l == segTree[pos].r)
return; pushUp(ls(pos));
pushUp(rs(pos)); //将ls(pos),rs(pos)中的元素存储到pos中
for(int i=;i < v[ls(pos)].size();++i)
v[pos].push_back(v[ls(pos)][i]);
for(int i=;i < v[rs(pos)].size();++i)
v[pos].push_back(v[rs(pos)][i]);
sort(v[pos].begin(),v[pos].end());//升序排列
}

  调用pushUp(1),将所有的pos 的 ls(pos),rs(pos) 节点信息更新到pos节点;

  调用完这个函数后,你会发现:

  v[1]:10,10,10,10([1,5]中的所有节点到其父节点的权值,根节点为null)

  v[2]:10,10([1,3]中的所有节点到其父节点的权值)

  v[3]:10,10([4,5]中的所有节点到其父节点的权值)

  v[4]:10([1,2]中的所有节点到其父节点的权值)

  v[5]:10([3,3]中的所有节点到其父节点的权值)

  v[6]:10([4,4]中的所有节点到其父节点的权值)

  v[7]:10([5,5]中的所有节点到其父节点的权值)

  v[8]:null(根节点为null)

  v[9]:10([2,2]中的所有节点到其父节点的权值)

  你会发现,v[i]中存的值就是[ tree[i].l , tree[i].r ]中所有节点与其父节点的权值;

  接下来就是询问操作了:

 int BS(int pos,int w)
{
int l=-,r=v[pos].size();
while(r-l > )
{
int mid=l+((r-l)>>);
if(v[pos][mid] <= w)
l=mid;
else
r=mid;
}
return l+;
}
int Query(int l,int r,int pos,int w)
{
if(v[pos][] > w)//当前区间的如果最小的值要 > w,直接返回0
return ;
if(segTree[pos].l == l && segTree[pos].r == r)
return BS(pos,w);//二分查找pos区间值 <= w 得个数(还记得pushUp()中的sort函数么? int mid=segTree[pos].mid();
if(r <= mid)
return Query(l,r,ls(pos),w);
else if(l > mid)
return Query(l,r,rs(pos),w);
else
return Query(l,mid,ls(pos),w)+Query(mid+,r,rs(pos),w);
}

AC代码:

 #include<bits/stdc++.h>
using namespace std;
#define ls(x) (x<<1)
#define rs(x) (x<<1|1)
#define INF 0x3f3f3f3f
#define mem(a,b) memset(a,b,sizeof(a))
const int maxn=1e5+; int n,m;
int fa[maxn];//fa[u]:u的父节点
int son[maxn];//son[u]:u的重儿子
int dep[maxn];//dep[u]:u的深度
int siz[maxn];//siz[u]:以u为根的子树节点个数
int tid[maxn];//tid[u]:u在线段树中的位置
int top[maxn];//top[u]:u所在重链的祖先节点
int e[maxn][];//e[i][0]与e[i][1]有条权值为e[i][2]的边
vector<int >v[maxn<<];//v[i]:存储线段树中i号节点的所有边的权值
int num;
int head[maxn];
struct Edge
{
int to;
int w;
int next;
}G[maxn<<];
void addEdge(int u,int v,int w)
{
G[num].to=v;
G[num].w=w;
G[num].next=head[u];
head[u]=num++;
}
struct SegmentTree
{
int l,r;
int mid()
{
return l+((r-l)>>);
}
}segTree[maxn<<];
void DFS1(int u,int f,int depth)
{
fa[u]=f;
son[u]=-;
siz[u]=;
dep[u]=depth;
for(int i=head[u];~i;i=G[i].next)
{
int v=G[i].to;
if(v == f)
continue;
DFS1(v,u,depth+); siz[u] += siz[v]; if(son[u] == - || siz[v] > siz[son[u]])
son[u]=v;
}
}
void DFS2(int u,int anc,int &k)
{
top[u]=anc;
tid[u]=++k;
if(son[u] == -)
return ;
DFS2(son[u],anc,k); for(int i=head[u];~i;i=G[i].next)
{
int v=G[i].to;
if(v != fa[u] && v != son[u])
DFS2(v,v,k);
}
}
void pushUp(int pos)
{
if(segTree[pos].l == segTree[pos].r)
return; pushUp(ls(pos));
pushUp(rs(pos)); //将ls(pos),rs(pos)中的元素存储到pos中
for(int i=;i < v[ls(pos)].size();++i)
v[pos].push_back(v[ls(pos)][i]);
for(int i=;i < v[rs(pos)].size();++i)
v[pos].push_back(v[rs(pos)][i]);
sort(v[pos].begin(),v[pos].end());//升序排列
}
void buildSegTree(int l,int r,int pos)
{
segTree[pos].l=l;
segTree[pos].r=r;
if(l == r)
return ; int mid=l+((r-l)>>);
buildSegTree(l,mid,ls(pos));
buildSegTree(mid+,r,rs(pos));
}
//将节点x在线段树中对应的pos位置的v中加入val
void Update(int x,int val,int pos)
{
if(segTree[pos].l == segTree[pos].r)
{
v[pos].push_back(val);//val加入到v[pos]中
return ;
}
int mid=segTree[pos].mid();
if(x <= mid)
Update(x,val,ls(pos));
else
Update(x,val,rs(pos));
}
int BS(int pos,int w)
{
int l=-,r=v[pos].size();
while(r-l > )
{
int mid=l+((r-l)>>);
if(v[pos][mid] <= w)
l=mid;
else
r=mid;
}
return l+;
}
int Query(int l,int r,int pos,int w)
{
if(v[pos][] > w)//当前区间的如果最小的值要 > w,直接返回0
return ;
if(segTree[pos].l == l && segTree[pos].r == r)
return BS(pos,w);//二分查找pos区间值 <= w 得个数(还记得pushUp()中的sort函数么? int mid=segTree[pos].mid();
if(r <= mid)
return Query(l,r,ls(pos),w);
else if(l > mid)
return Query(l,r,rs(pos),w);
else
return Query(l,mid,ls(pos),w)+Query(mid+,r,rs(pos),w);
}
int Find(int u,int v,int w)//查询节点u到节点v之间权值小于等于w得路径个数
{
int ans=;
int topU=top[u];
int topV=top[v];
while(topU != topV)
{
if(dep[topU] > dep[topV])
{
swap(u,v);
swap(topU,topV);
}
ans += Query(tid[top[v]],tid[v],,w);
v=fa[topV];
topV=top[v];
}
if(u == v)
return ans;
if(dep[u] > dep[v])
swap(u,v);
return ans+Query(tid[son[u]],tid[v],,w);
}
void Solve()
{
DFS1(,,);
int k=;
DFS2(,,k); buildSegTree(,k,); for(int i=;i < n;++i)
{
if(dep[e[i][]] > dep[e[i][]])
swap(e[i][],e[i][]);//令fa[e[i][1]] = e[i][0],方便更新操作
Update(tid[e[i][]],e[i][],);//将e[i][2]加入到tid[e[i][1]]中
}
pushUp();//更新线段树中所有的pos for(int i=;i <= m;++i)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
printf("%d\n",Find(u,v,w));
}
}
void Init()
{
num=;
mem(head,-);
for(int i=;i < *maxn;++i)
v[i].clear();
}
int main()
{
// freopen("C:\\Users\\hyacinthLJP\\Desktop\\in&&out\\contest","r",stdin);
while(~scanf("%d%d",&n,&m))
{
Init();
for(int i=;i < n;++i)
{
scanf("%d%d%d",e[i]+,e[i]+,e[i]+);
addEdge(e[i][],e[i][],e[i][]);
addEdge(e[i][],e[i][],e[i][]);
}
Solve();
}
return ;
}

2019南昌邀请赛网络预选赛 J.Distance on the tree(树链剖分)的更多相关文章

  1. 2019南昌邀请赛网络赛:J distance on the tree

    1000ms 262144K   DSM(Data Structure Master) once learned about tree when he was preparing for NOIP(N ...

  2. 2019年ICPC南昌网络赛 J. Distance on the tree 树链剖分+主席树

    边权转点权,每次遍历到下一个点,把走个这条边的权值加入主席树中即可. #include<iostream> #include<algorithm> #include<st ...

  3. 计蒜客 2019南昌邀请网络赛J Distance on the tree(主席树)题解

    题意:给出一棵树,给出每条边的权值,现在给出m个询问,要你每次输出u~v的最短路径中,边权 <= k 的边有几条 思路:当时网络赛的时候没学过主席树,现在补上.先树上建主席树,然后把边权交给子节 ...

  4. 2019南昌邀请赛网络预选赛 M. Subsequence

    传送门 题意: 给出一个只包含小写字母的串 s 和n 个串t,判断t[i]是否为串 s 的子序列: 如果是,输出"YES",反之,输出"NO": 坑点: 二分一 ...

  5. 2019南昌邀请赛网络预选赛 I. Max answer(单调栈+暴力??)

    传送门 题意: 给你你一序列 a,共 n 个元素,求最大的F(l,r): F(l,r) = (a[l]+a[l+1]+.....+a[r])*min(l,r); ([l,r]的区间和*区间最小值,F( ...

  6. 计蒜客 38229.Distance on the tree-1.树链剖分(边权)+可持久化线段树(区间小于等于k的数的个数)+离散化+离线处理 or 2.树上第k大(主席树)+二分+离散化+在线查询 (The Preliminary Contest for ICPC China Nanchang National Invitational 南昌邀请赛网络赛)

    Distance on the tree DSM(Data Structure Master) once learned about tree when he was preparing for NO ...

  7. 南昌网络赛J. Distance on the tree 树链剖分

    Distance on the tree 题目链接 https://nanti.jisuanke.com/t/38229 Describe DSM(Data Structure Master) onc ...

  8. 南昌网络赛J. Distance on the tree 树链剖分+主席树

    Distance on the tree 题目链接 https://nanti.jisuanke.com/t/38229 Describe DSM(Data Structure Master) onc ...

  9. 2019南昌网络赛 J Distance on the tree 主席树+lca

    题意 给一颗树,每条边有边权,每次询问\(u\)到\(v\)的路径中有多少边的边权小于等于\(k​\) 分析 在树的每个点上建\(1​\)到\(i​\)的权值线段树,查询的时候同时跑\(u,v,lca ...

随机推荐

  1. DataSnap 多层返回数据集分析FireDAC JSON

    采用服务器返回数据,一种是返回字符串数据例如JSON,跨平台跨语言,任何语言调用都支持兼容,类似WEBService. 第二种是紧密结合c++builder语言,传输DataSet,可以是Client ...

  2. 从0开始的Python学习006流程控制

    流程控制语句 Python中有三种控制流程语句: if.for.和while. if语句 使用if语句来校验一个条件,如果条件为真(True),运行if-块,如果为假(False),运行else-块. ...

  3. nginx多server配置记录

    直接在配置文件(/etc/nginx/nginx.conf)中添加如下代码: server { listen 8080; server_name 192.168.100.174:8080; root ...

  4. serversql数据库的查询操作

    sql数据库 *:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: 0 !importan ...

  5. c/c++ 多线程 等待一次性事件 packaged_task用法

    多线程 等待一次性事件 packaged_task用法 背景:不是很明白,不知道为了解决什么业务场景,感觉std::asynck可以优雅的搞定一切,一次等待性事件,为什么还有个packaged_tas ...

  6. c/c++ 多线程 参数传递

    多线程 参数传递 1,值传递,拷贝一份新的给新的线程.线程1中有个int变量a,在线程1中启动线程2,参数是a的值,这时就会拷贝a,线程1和线程2不共享a. 2,引用传递,不拷贝一份新的给新的线程.线 ...

  7. c/c++ 多线程 detach的困惑

    多线程 detach的困惑 求大神解答: 1,当在一个函数里启动一个线程后,并detach了 2,detach的线程里使用了这个函数里new出来的一个对象 3,detach后,delete了这个对象 ...

  8. 6.1Python数据处理篇之pandas学习系列(一)认识pandas

    目录 目录 (一)介绍与测试 2.作用: 3.导入的格式 4.小测试 (二)数据类型 1.两种重要的数据类型 2.pandas与numpy的比较 目录 (一)介绍与测试 号称处理数据与分析数据最好的第 ...

  9. 【字】biang

    biang biang面的名字由来:biangbiang面是陕西关中地区的一中地区美食,因为在做这种面时会发出biang biang的声音,biang biang面因此得名.biang字简体共有42笔 ...

  10. 平滑升级你的Nginx

    1.概述(可以直接跳过看第2部分) Nginx方便地帮助我们实现了平滑升级.其原理简单概括,就是: (1)在不停掉老进程的情况下,启动新进程. (2)老进程负责处理仍然没有处理完的请求,但不再接受处理 ...