2019年ICPC南昌网络赛 J. Distance on the tree(树链剖分+主席树 查询路径边权第k大)
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2019年ICPC南昌网络赛 J. Distance on the tree(树链剖分+主席树 查询路径边权第k大)相关的知识,希望对你有一定的参考价值。
DSM(Data Structure Master) once learned about tree when he was preparing for NOIP(National Olympiad in Informatics in Provinces) in Senior High School. So when in Data Structure Class in College, he is always absent-minded about what the teacher says.
The experienced and knowledgeable teacher had known about him even before the first class. However, she didnt wish an informatics genius would destroy himself with idleness. After she knew that he was so interested in ACM(ACM International Collegiate Programming Contest), she finally made a plan to teach him to work hard in class, for knowledge is infinite.
This day, the teacher teaches about trees." A tree with nn nodes, can be defined as a graph with only one connected component and no cycle. So it has exactly n-1n−1 edges..." DSM is nearly asleep until he is questioned by teacher. " I have known you are called Data Structure Master in Graph Theory, so here is a problem. "" A tree with nn nodes, which is numbered from 11 to nn. Edge between each two adjacent vertexes uu and vv has a value w, youre asked to answer the number of edge whose value is no more than kk during the path between uu and vv."" If you cant solve the problem during the break, we will call you DaShaMao(Foolish Idiot) later on."
The problem seems quite easy for DSM. However, it can hardly be solved in a break. Its such a disgrace if DSM cant solve the problem. So during the break, he telephones you just for help. Can you save him for his dignity?
Input
In the first line there are two integers n,mn,m, represent the number of vertexes on the tree and queries(2 \\le n \\le 10^5,1 \\le m \\le 10^52≤n≤105,1≤m≤105)
The next n-1n−1 lines, each line contains three integers u,v,wu,v,w, indicates there is an undirected edge between nodes uu and vv with value ww. (1 \\le u,v \\le n,1 \\le w \\le 10^91≤u,v≤n,1≤w≤109)
The next mm lines, each line contains three integers u,v,ku,v,k , be consistent with the problem given by the teacher above. (1 \\le u,v \\le n,0 \\le k \\le 10^9)(1≤u,v≤n,0≤k≤109)
Output
For each query, just print a single line contains the number of edges which meet the condition.
样例输入1复制
3 3
1 3 2
2 3 7
1 3 0
1 2 4
1 2 7
样例输出1复制
0
1
2
样例输入2复制
5 2
1 2 1000000000
1 3 1000000000
2 4 1000000000
3 5 1000000000
2 3 1000000000
4 5 1000000000
样例输出2复制
2
4
题意:
你一棵树,n个节点(n<1e5),m次询问(m<1e5),每次询问<u,v,k>
即,从节点u到节点v的路上,有多少条边的长度小于等于k
分析:
树链剖分+主席树
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define pb(x) push_back(x)
#define N 200005
#define inf 0x3f3f3f3f
#define mp(x,y) make_pair(x,y)
//查询u->v的路径中有多少条边的权值小于w
//根节点赋值无穷大,其他的边将 边权赋给下顶点。
vector<int>V;
struct node
int to,nt,w;
g[N*3];
struct Q
int x,y,w;
q[N];
int tot;
int head[N];
void addedg(int x,int y,int w)
g[tot].to=y;
g[tot].nt=head[x];
g[tot].w=w;
head[x]=tot++;
int sz[N],son[N],dep[N],num[N];//子树大小,重儿子,深度,dfs序为id对应的节点。
int fa[N],top[N],val[N],rnk[N];//父节点,链顶,权值,dfs序
void dfs1(int u,int f,int de)//第一次dfs出dep fa 和重儿子。
dep[u]=de;
fa[u]=f;
sz[u]=1;
int mx=-1;
for(int i=head[u]; i+1; i=g[i].nt)
int v=g[i].to;
if(v==f)
continue;
val[v]=g[i].w;
dfs1(v,u,de+1);
sz[u]+=sz[v];
if(sz[v]>mx)
mx=sz[v],son[u]=v;
int id;
void dfs2(int u,int Top)//第二次优先走重儿子,将树上的链展开。
top[u]=Top;
rnk[u]=++id;
num[id]=u;
if(!son[u])
return ;
dfs2(son[u],Top);
for(int i=head[u]; i+1; i=g[i].nt)
int v=g[i].to;
if(v==fa[u]||v==son[u])
continue;
dfs2(v,v);
int cnt;
int sum[N*36],ls[N*36],rs[N*36],T[N*36];//主席树部分
void build(int &rt,int l,int r)
rt=++cnt;
sum[rt]=0;
if(l==r)
return ;
int m=(l+r)>>1;
build(ls[rt],l,m);
build(rs[rt],m+1,r);
void upd(int &now,int pre,int l,int r,int pos)
now=++cnt;
sum[now]=sum[pre]+1;
ls[now]=ls[pre],rs[now]=rs[pre];
if(l==r)
return ;
int m=(l+r)>>1;
if(pos<=m)
upd(ls[now],ls[pre],l,m,pos);
else
upd(rs[now],rs[pre],m+1,r,pos);
int ask(int now,int pre,int l,int r,int pos)
if(r<=pos)
return sum[now]-sum[pre];
int m=(l+r)>>1;
if(pos<=m)
return ask(ls[now],ls[pre],l,m,pos);
else
return ask(ls[now],ls[pre],l,m,pos)+ask(rs[now],rs[pre],m+1,r,pos);
int SZ;
void init(int n,int m)//将询问和原始的边权离散化
for(int i=2; i<=n; i++)
val[i]=lower_bound(V.begin(),V.end(),val[i])-V.begin()+1;
for(int i=1; i<=m; i++)
q[i].w=lower_bound(V.begin(),V.end(),q[i].w)-V.begin()+1;
SZ=V.size()+100;
build(T[0],1,SZ);
val[1]=V.size()+5;
for(int i=1; i<=n; i++)
upd(T[i],T[i-1],1,SZ,val[num[i]]);
int query(int u,int v,int pos)//对路径的查询,基于边权。
int ans=0;
while(top[u]!=top[v])
if(dep[top[u]]<dep[top[v]])
swap(u,v);
ans+=ask(T[rnk[u]],T[rnk[top[u]]-1],1,SZ,pos);//这里要把top[u]算进来,所以要减一
u=fa[top[u]];
if(dep[u]<dep[v])
swap(u,v);
ans+=ask(T[rnk[u]],T[rnk[v]],1,SZ,pos);//这里v不能减一,因为v不能算进来(v点所代表的边权不是要求路径上的)。
return ans;
int main()
int n,m;
scanf("%d",&n);scanf("%d",&m);
memset(head,-1,sizeof(head));
for(int i=1; i<n; i++)
int x,y,w;
scanf("%d%d%d",&x,&y,&w);
addedg(x,y,w);
addedg(y,x,w);
V.pb(w);
for(int i=1; i<=m; i++)
scanf("%d%d%d",&q[i].x,&q[i].y,&q[i].w);
V.pb(q[i].w);
sort(V.begin(),V.end());
V.erase(unique(V.begin(),V.end()),V.end());
dfs1(1,0,1);
dfs2(1,1);
init(n,m);
for(int i=1; i<=m; i++)
printf("%d\\n",query(q[i].x,q[i].y,q[i].w));
#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
#define lson(x) ((x<<1))
#define rson(x) ((x<<1)+1)
#define mp(x,y) make_pair(x,y)
#define rep(i,a,b) for(register int i=(a);i<=(b);i++)
#define dep(i,a,b) for(register int i=(a);i>=(b);i--)
using namespace std;
const int N = 100005;
const int maxn = 100005;
int n,m;
int dep[N],siz[N],fa[N],id[N],son[N],val[N],top[N]; //top 近期的重链父节点
int num;
vector<pair<int,int> > v[N];
int a[N];
struct nod
int id,v,fg;
int x,y;
bool operator<(nod aa)const
return v<aa.v||(v==aa.v&&fg<aa.fg);
q[maxn<<1];
int as[maxn];
struct tree
int x,y,val;
void read()
scanf("%d%d%d",&x,&y,&val);
;
tree e[N];
void dfs1(int u, int f, int d)
dep[u] = d;
siz[u] = 1;
son[u] = 0;
fa[u] = f;
for (int i = 0; i < v[u].size(); i++)
int ff = v[u][i].first;
if (ff == f) continue;
q[ff].v=v[u][i].second;
q[ff].id=ff;
q[ff].x=ff;
q[ff].y=u;
dfs1(ff, u, d + 1);
siz[u] += siz[ff];
if (siz[son[u]] < siz[ff])
son[u] = ff;
void dfs2(int u, int tp)
top[u] = tp;
id[u] = ++num;
if (son[u]) dfs2(son[u], tp);
for (int i = 0; i < v[u].size(); i++)
int ff = v[u][i].first;
if (ff == fa[u] || ff == son[u]) continue;
dfs2(ff, ff);
int lb(int x)return x&(-x);
void add(int x,int v)
while(x<N)
a[x]+=v;
x+=lb(x);
int query(int x)
int sum=0;
while(x)
sum+=a[x];
x-=lb(x);
return sum;
int Yougth(int u, int v)
//cout<<"OK";
int tp1 = top[u], tp2 = top[v];
int ans = 0;
while (tp1 != tp2)
//printf("YES\\n");
if (dep[tp1] < dep[tp2])
swap(tp1, tp2);
swap(u, v);
ans+=query(id[u])-query(id[tp1]-1);
u = fa[tp1];
tp1 = top[u];
if (u == v) return ans;
if (dep[u] > dep[v]) swap(u, v);
ans += query(id[v])-query(id[son[u]]-1);
//cout<<"OOOOK"<<endl;;
return ans;
void Clear(int n)
for(int i=0;i<=n;i++)
a[i]=0;
v[i].clear();
int main()
int T,cas=1;
scanf("%d%d",&n,&m);
for(int i=1;i<n;i++)
e[i].read();
q[i].fg=0;
v[e[i].x].push_back(mp(e[i].y,e[i].val));
v[e[i].y].push_back(mp(e[i].x,e[i].val));
num = 0;
q[1].v=inf;
q[1].id=1;
q[1].x=1;
q[1].y=1;
dfs1(1,0,1);
//rep(i,1,n)
//cout<<q[i].fg<<" "<<q[i].x<<endl;
dfs2(1,1);
for (int i = 1; i < n; i++)
if (dep[e[i].x] < dep[e[i].y]) swap(e[i].x, e[i].y);
val[id[e[i].x]] = e[i].val;
rep(i,1,m)
scanf("%d%d%d",&q[i+n].x,&q[i+n].y,&q[i+n].v);
q[i+n].id=i+n;
q[i+n].fg=1;
//cout<<"*"<<endl;
sort(q+1,q+n+m+1);
rep(i,1,n+m)
//cout<<i<<"*"<<q[i].fg<<" "<<q[i].id<<" "<<q[i].x;
if(q[i].fg)
as[q[i].id-n]=Yougth(q[i].x,q[i].y);
else
int u=q[i].x;int v=q[i].y;
int tp1 = top[u], tp2 = top[v];
if (dep[tp1] < dep[tp2])
swap(tp1, tp2);
swap(u, v);
//cout<<u<<" "<<id[u]<<endl;
add(id[u],1);
//cout<<"*"<<endl;;
rep(i,1,m)
printf("%d\\n",as[i]);
//Clear(n);
return 0;
以上是关于2019年ICPC南昌网络赛 J. Distance on the tree(树链剖分+主席树 查询路径边权第k大)的主要内容,如果未能解决你的问题,请参考以下文章