南昌邀请赛网络赛 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 didn't 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, you're asked to answer the number of edge whose value is no more than kk during the path between uu and vv."" If you can't 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. It's such a disgrace if DSM can't 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

题目大意:题目让求出路径上边权小于k的边有多少条。

解题思路:听说过树链剖分,知道思想,但是一直没写过,以为用到的话应该能写出来。。。

其实点权还是比较好整的,但是第一次写就出现了边权,需要将根节点设置为0或者无穷大。。

比赛的时候想到了要用剖分+主席树,但是剖分后不知道怎么处理边权,,,没写出来。

对与边权:查询的时候,如果查询的两个点不再一条链上,那么深度小的那个端点是属于要统计的范围的。

如果在一条链上的话,深度小的端点所代表的边就不是我们要求的路径上的边。

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define sca(x) scanf("%d",&x)
#define rep(i,j,k) for(LL i=j;i<=k;i++)
#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;
    sca(n),sca(m);
    memset(head,-1,sizeof(head));
    for(int i=1;i<n;i++)
    {
        int x,y,w;
        sca(x),sca(y),sca(w);
        addedg(x,y,w);
        addedg(y,x,w);
        V.pb(w);
    }
    for(int i=1;i<=m;i++)
    {
        sca(q[i].x),sca(q[i].y),sca(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));
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值