Mr. Peter lives in the magical lands of ACM City. The city consists of N districts and
N – 1 two-way roads. Each road connects a pair of districts, and for each road we know the
length of the road. All districts are connected to each other, meaning Mr. Peter can always travel
from any district A to any district B.
The ACM City has a very special rule for travelling. If anyone wants to travel from district
A to district B, they must pay the tax equal to the median of the length of all roads from
district A to district B. For example, this ACM City of 6 districts.
All circles represent districts and lines represent roads connecting two districts.
Numbers inside the circles are the number of each district and numbers above the lines are the
length of each road. If Mr. Peter travels from district 1 to district 3, the length of all roads would
be 1, 5, and 9, therefore he must pay the tax of 5 dollars. However, if Mr. Peter travels from
district 6 to district 4, he must pay 4.5 dollars, for the length of the roads are 1, 4, 5, and 7.
Mr. Peter is a very curious person. He wants to know the tax he needs to pay to travel
from any two districts. However, Mr. Peter is too lazy to calculate the tax himself, so he asks
you, the best programmer in the city, to calculate the tax Mr. Peter needs to pay from district A
to district B.
Note: The ‘Median’ is the middle value of a sorted list of numbers. For example, the
median of the list 1, 5, 7, 7, 9, 10, 16 is 7. If the amount of numbers in the list is even, the median
is the sum of two middle numbers divided by 2. For example, the median of the list 1, 5, 7, 9,
10, 17, 28, 30 is (9+10)/2, which is 9.5.
Given the ACM City of N districts, for each question, find the median of the length of all
path from district A to district B.
Input
The first line contains number of test case T (T ≤ 15). Then for each test case:
The first line contains one integer N (2 ≤ N ≤ 50 000) — the number of districts.
The next N – 1 lines contain three integers Ui, Vi, Wi (1 ≤ Ui, Vi ≤ N, Ui ≠ Vi,
1 ≤ Wi ≤ 100 000) — the roads connecting district Ui, and Viwith the length Wi .
The next line contains one integer Q (1 ≤ Q ≤ 100 000) — the number of questions Mr.
Peter wants to ask.
The next Q lines contain two integers Ai, Bi (1 ≤ Ai, Bi ≤ N, Ai ≠ Bi) — the questions Mr.
Peter wants to ask how much he needs to pay to travel from district Ai to district Bi.
Output
Print Q lines. In each line contains one real number with one digit after the decimal point
— the tax Mr. Peter needs to pay to travel from district Ai to district Bi.
Example
Input
1
6
1 2 9
2 5 5
2 4 7
3 5 1
3 6 4
3
1 3
4 6
2 6
Output
5.0
4.5
4.0
一、原题地址
二、大致题意
给出一棵树,n个顶点每条边有自己的权值。现在有Q个询问,每次给出一组(u,v),要求输出u,v两点之间边权值的中位数。
三、大致思路
要求中位数,实际上和求区间K值是一样的问题,但是现在转移到了树上。
之前求解树上两点之间距离时,运用的是找出两点的LCA,然后通过deep[u]+deep[v]-2*deep[lca]的方式求解,这里的思路就类似于这种思想,先以某点为根,如点1,跑DFS建立可持久化的数据结构(以下采取了划分树)。注意,版本的继承来源于他们的父节点而不是顺着DFS序建立!
这样的话可以发现,之前求距离的性质在这里的可持久化结构上也是可以运用的,当前u,v两点继承下来的数据相加再减去他们lca处的数据,那么就搞出了这一段上的数据,接下来就是常规的K-th操作了。
四、冗长的代码
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;
#define LL long long int
int n,T;
const int maxn=1e5+5;
struct Edge
{
int v, next;
LL w;
}e[maxn<<1];
int cnt;
int rt[maxn];
struct Node
{
int ls, rs, l, r;
LL sum;
}tr[maxn*33];
void PushUp(int i)
{
tr[i].sum = tr[tr[i].ls].sum + tr[tr[i].rs].sum;
}
int Build(int l,int r)
{
int pos = ++cnt;
if (l == r)
{
tr[pos].sum = 0;
return pos;
}
int mid = (l + r) >> 1;
tr[pos].ls = Build(l,mid);
tr[pos].rs = Build(++mid,r);
return pos;
}
//第零号版本的线段树
int Query(int u,int v,int lca,int l,int r,int k)
{
if(l==r)return l;
int mid = (l + r) >> 1;
int lsum=tr[tr[u].ls].sum + tr[tr[v].ls].sum-2*tr[tr[lca].ls].sum;
if (lsum>=k) return Query(tr[u].ls,tr[v].ls,tr[lca].ls,l,mid,k);
else if(lsum<k)return Query(tr[u].rs,tr[v].rs,tr[lca].rs,mid+1,r,k-lsum);
}
//查询第ed个版本的线段树[ql,qr]上的和
int update(int ed,int l,int r,int p,LL k)
{
int pos = ++cnt;
tr[pos].sum=tr[ed].sum;
if (l == r)
{
tr[pos].sum += k;
return pos;
}
tr[pos].ls = tr[ed].ls;
tr[pos].rs = tr[ed].rs;
int mid = (l + r) >> 1;
if (p <= mid) tr[pos].ls = update(tr[ed].ls,l,mid,p,k);
else tr[pos].rs = update(tr[ed].rs,++mid,r,p,k);
PushUp(pos);
return pos;
}
//单点更新p点的值加上k
//返回值为新的版本树的根节点
int tot,head[maxn],fa[maxn],top[maxn],deep[maxn],siz[maxn],son[maxn],val[maxn];
void addedge(int u, int v, LL w)
{
e[tot].v = v, e[tot].w = w;
e[tot].next = head[u];
head[u] = tot++;
}
void dfs1(int x)
{
deep[x]=deep[fa[x]]+1;siz[x]=1;
son[x]=0;
for(int i=head[x];i!=-1;i=e[i].next)
{
int to=e[i].v;
if(fa[x]!=to&&!fa[to])
{
fa[to]=x;
rt[to]=update(rt[fa[to]],1,100000,e[i].w,1);
dfs1(to);
siz[x]+=siz[to];
if(siz[son[x]]<siz[to])son[x]=to;
}
}
}
void dfs2(int x)
{
if(x==son[fa[x]])top[x]=top[fa[x]];
else top[x]=x;
for(int i=head[x];i!=-1;i=e[i].next)if(fa[e[i].v]==x)dfs2(e[i].v);
}
void init_Tree(int root)
{
deep[root]=0;for(int i=1;i<=n;i++)fa[i]=0;
rt[0]=Build(1,100000);
dfs1(root),dfs2(root);
}
int query(int x,int y)//返回lca
{
for(;top[x]!=top[y];deep[top[x]]>deep[top[y]]?x=fa[top[x]]:y=fa[top[y]]);
return deep[x]<deep[y]?x:y;
}
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
for(int i=1;i<=n;i++)head[i]=-1;
cnt=tot=0;
for(int i=1;i<=n-1;i++)
{
int u,v,w;
scanf("%d %d %d",&u,&v,&w);
addedge(u,v,w);
addedge(v,u,w);
}
init_Tree(1);
int Q;
scanf("%d",&Q);
while(Q--)
{
int u,v;
scanf("%d %d",&u,&v);
int lca=query(u,v);
int dis=deep[u]+deep[v]-2*deep[lca];
double ans;
if(dis&1)
{
ans=Query(rt[u],rt[v],rt[lca],1,100000,dis/2+1);
printf("%.1f\n",ans);
}
else
{
ans=Query(rt[u],rt[v],rt[lca],1,100000,dis/2)+Query(rt[u],rt[v],rt[lca],1,100000,dis/2+1);
printf("%.1f\n",ans/2.0);
}
}
}
}
/*
*/