【xsy1197】 树 二分+点分树+二分

题目大意:给你一棵$n$个点的带权树和正整数$K$,求每个点到其它所有点距离中第$K$大的数值。

其中,边权$≤10000$,$n≤50000$。

我们通过原树构建一棵点分治树,令$fa[u]$为$u$在点分树上的$father$。

对于每个点$u$,我们维护两个有序数组$f$和$g$。

其中$f[i]$表示以$u$为根的点分树中,距离$u$第$i$近的距离。(显然里面有$siz[u]$个数值)

$g[i]$表示以$u$为根的点分树中,距离$fa[u]$第i近的距离。

我们二分答案,设当前二分到的值为$p$,我们要求所有与$u$距离$≤p$的数量。

然后答案显然为$\sum_{v∈ancestor[u]} (\sum_{f[v][i]≤p-dis(v,u)}1-\sum_{g[v][i]≤p-dis(fa[v],u)}1)$

这样单次询问的时间复杂度显然是$O(log^3n)$的。

然后时间复杂度就是$O(n\ log^3\ n)$。

完结撒花

  1 #include<bits/stdc++.h>
  2 #define M 50005
  3 #define INF 19890604
  4 using namespace std;
  5
  6 struct edge{int u,v,next;}e[M*2]={0}; int head[M]={0},use=0;
  7 void add(int x,int y,int z){use++;e[use].u=y;e[use].v=z;e[use].next=head[x];head[x]=use;}
  8 int n,k;
  9 int f[M][20]={0},d[M]={0},dep[M]={0};
 10
 11 void dfs(int x,int fa){
 12     f[x][0]=fa; dep[x]=dep[fa]+1;
 13     for(int i=1;i<20;i++) f[x][i]=f[f[x][i-1]][i-1];
 14     for(int i=head[x];i;i=e[i].next) if(e[i].u!=fa){
 15         d[e[i].u]=d[x]+e[i].v;
 16         dfs(e[i].u,x);
 17     }
 18 }
 19 int getlca(int x,int y){
 20     if(dep[x]<dep[y]) swap(x,y); int cha=dep[x]-dep[y];
 21     for(int i=19;~i;i--) if((1<<i)&cha) x=f[x][i];
 22     for(int i=19;~i;i--) if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
 23     if(x==y) return x; return f[x][0];
 24 }
 25 int getdis(int x,int y){
 26     int lca=getlca(x,y);
 27     return d[x]+d[y]-2*d[lca];
 28 }
 29
 30 int vis[M]={0},siz[M]={0},minn=INF,minid=0;
 31 void dfssiz(int x,int fa){
 32     siz[x]=1;
 33     for(int i=head[x];i;i=e[i].next)
 34     if(e[i].u!=fa&&vis[e[i].u]==0){
 35         dfssiz(e[i].u,x);
 36         siz[x]+=siz[e[i].u];
 37     }
 38 }
 39 void dfsmin(int x,int fa,int fsiz){
 40     int maxn=fsiz-siz[x];
 41     for(int i=head[x];i;i=e[i].next)
 42     if(e[i].u!=fa&&vis[e[i].u]==0){
 43         dfsmin(e[i].u,x,fsiz);
 44         maxn=max(maxn,siz[e[i].u]);
 45     }
 46     if(maxn<minn) minn=maxn,minid=x;
 47 }
 48 int makeroot(int x){
 49     dfssiz(x,0);
 50     minn=INF; minid=0;
 51     dfsmin(x,0,siz[x]);
 52     return minid;
 53 }
 54 vector<int> F[M],G[M];
 55
 56 void addvec(int x,int fa,int X,int FA,int nowdis){
 57     F[X].push_back(getdis(x,X));
 58     G[X].push_back(getdis(x,FA));
 59     for(int i=head[x];i;i=e[i].next)
 60     if(e[i].u!=fa&&vis[e[i].u]==0)
 61     addvec(e[i].u,x,X,FA,nowdis+e[i].v);
 62 }
 63 int fa[M]={0};
 64 void build(int x,int Fa){
 65     x=makeroot(x); vis[x]=1; fa[x]=Fa;
 66     addvec(x,fa[x],x,fa[x],0);
 67     sort(F[x].begin(),F[x].end());
 68     sort(G[x].begin(),G[x].end());
 69     for(int i=head[x];i;i=e[i].next) if(vis[e[i].u]==0){
 70         build(e[i].u,x);
 71     }
 72 }
 73
 74 bool check(int x,int mid){
 75     int res=-1,X=x;
 76     for(;x;x=fa[x]){
 77
 78         res+=upper_bound(F[x].begin(),F[x].end(),mid-getdis(X,x))-F[x].begin();
 79         if(fa[x]) res-=upper_bound(G[x].begin(),G[x].end(),mid-getdis(X,fa[x]))-G[x].begin();
 80     }
 81     return res>=k;
 82 }
 83
 84 int main(){
 85     scanf("%d%d",&n,&k);
 86     for(int i=1;i<n;i++){
 87         int x,y,z; scanf("%d%d%d",&x,&y,&z);
 88         add(x,y,z); add(y,x,z);
 89     }
 90     dfs(1,0);
 91     build(1,0);
 92     for(int i=1;i<=n;i++){
 93         int l=0,r=10000*n;
 94         while(l<r){
 95             int mid=(l+r)>>1;
 96             if(check(i,mid)) r=mid;
 97             else l=mid+1;
 98         }
 99         printf("%d\n",l);
100     }
101 }

原文地址:https://www.cnblogs.com/xiefengze1/p/9800854.html

时间: 2024-08-30 02:21:59

【xsy1197】 树 二分+点分树+二分的相关文章

bzoj2117 [ 2010国家集训队 ] -- 点分树+二分答案

考虑点分树. 求出每个重心所管辖的范围内的每个点到它的距离,建成点分树. 查询时二分答案,然后问题就转化为求到x的距离<=d的点的个数. 在点分树上暴力往上跑就行了,注意去重. 时间复杂度:O(nlog3n) 代码: 1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<algorithm> 5 #include<vector> 6 #include&l

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

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

二分查找判定树

5.二分查找判定树 二分查找过程可用二叉树来描述:把当前查找区间的中间位置上的结点作为根,左子表和右子表中的结点分别作为根的左子树和右子树.由此得到的二叉树,称为描述二分查找的判定树(Decision Tree)或比较树(Comparison Tree). 注意: 判定树的形态只与表结点个数n相关,而与输入实例中R[1..n].keys的取值无关. [例]具有11个结点的有序表可用下图所示的判定树来表示. (1)二分查找判定树的组成 ①圆结点即树中的内部结点.树中圆结点内的数字表示该结点在有序表

二分+树状数组/线段树(区间更新) HDOJ 4339 Query

题目传送门 题意:给两串字符串,操作1:替换其中一个字符串的某个位置的字符 操作2:查询从p开始相等的最长连续长度 分析:树状数组可以维护一个区间内公共长度(连续)的情况,查询时用二分查找最远的端点即可.还可以用线段树去做,线段树能处理的问题很多,这题只要往右区间合并就行了. 收获:1.线段树的区间合并又做一题(虽然我写的还没AC) 2. 树状数组写起来方便又奇效! 代码1(树状数组): /************************************************ * Au

HDU 5592 ZYB&#39;s Game 【树状数组】+【二分】

<题目链接> 题目大意: 给你一个由1~n,n个数组成的序列,给出他们每个的前缀逆序数,现在要求输出这个序列. 解题分析: 由前缀逆序数很容易能够得到每个数的逆序数.假设当前数是i,它前面比它小的数为a[i]( i - 1 - i的逆序数即可),我们不难知道,i在前i个数中是第i+1大的.然后我们从后往前考虑,每次都能确定一个位置的数的大小,根据当前位置i的数在 1~i 的数的大小,我们用二分查找快速聪当前还未分配的数中给它分配相应大小的数值,然后将这个数值从可分配的数中剔除,防止对前面的数造

2019杭电多校第四场hdu6621 K-th Closest Distance(二分答案+主席树)

K-th Closest Distance 题目传送门 解题思路 二分答案+主席树 先建主席树,然后二分答案mid,在l和r的区间内查询[p-mid, p+mid]的范围内的数的个数,如果大于k则说明这个范围内存在第k小的数,r=mid,否则不存在,l=mid+1. 代码如下 #include <bits/stdc++.h> #define INF 0x3f3f3f3f using namespace std; typedef long long ll; inline int read(){

分治法--二分查找、乘方、斐波那契数

1.二分查找 常见错误: 死循环:循环体外的初始化条件,与循环体内的迭代步骤, 都必须遵守一致的区间规则,也就是说,如果循环体初始化时,是以左闭右开区间为边界的,那么循环体内部的迭代也应该如此.如果两者不一致,会造成程序的错误. 溢出:middle = left + (right - left) / 2 终止条件:一般来说,如果左闭右闭,则left<=right: 如果一开一闭,则left<right: 关键看left能不能等于right,而且要考虑实际情况,有时不能这样简单终结,会出现死循环

【BZOJ3435】[Wc2014]紫荆花之恋 替罪点分树+SBT

[BZOJ3435][Wc2014]紫荆花之恋 Description 强强和萌萌是一对好朋友.有一天他们在外面闲逛,突然看到前方有一棵紫荆树.这已经是紫荆花飞舞的季节了,无数的花瓣以肉眼可见的速度从紫荆树上长了出来.仔细看看的话,这个大树实际上是一个带权树.每个时刻它会长出一个新的叶子节点.每个节点上有一个可爱的小精灵,新长出的节点上也会同时出现一个新的小精灵.小精灵是很萌但是也很脆弱的生物,每个小精灵 i 都有一个感受能力值Ri ,小精灵 i, j 成为朋友当且仅当在树上 i 和 j 的距离

FZU 2082 过路费(树链抛分)

FZU 2082 过路费 题目链接 树链抛分修改边的模板题 代码: #include <cstdio> #include <cstring> #include <vector> using namespace std; typedef long long ll; const int N = 50005; int dep[N], id[N], sz[N], top[N], son[N], fa[N], idx; int n, m; ll bit[N]; struct Ed