南昌网络赛 Distance on the tree(树上主席树,求 路径上边权 小于等于 k 的数量)

题目链接:芝麻开门

题意:给你一棵树,有q 个询问,问 u , v 路径上 边权 <= k 的边的数量;

思路:用主席树 维护 边权,代表 u 节点 到 根节点的信息,故有  c[lson[u]] + c[lson[v]] - 2 * c[lson[LCA]]; (这个画一画就能理解为什么和点权不一样)

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define debug(x) cout << "[" << #x <<": " << (x) <<"]"<< endl
#define pii pair<int,int>
#define clr(a,b) memset((a),b,sizeof(a))
#define rep(i,a,b) for(int i = a;i < b;i ++)
#define pb push_back
#define MP make_pair
#define LL long long
#define ull unsigned LL
#define ls i << 1
#define rs i << 1 + 1
#define INT(t) int t; scanf("%d",&t)

using namespace std;

const int maxn = 1e5 + 10;
const int M = maxn * 40;
int T[maxn];///T[i]表示第i颗线段树的顶结点
int lson[M],rson[M],c[M];///C[i]就代表第i个点的权值,即上述权值线段树的sum
int X[maxn];     /// lson[i], rson[i] 表示第 i 个点的左儿子,右儿子是谁
int tot = 0,en;          /// tot 用于动态开点

int head[maxn];
struct xx{
    int u,v,w,nex;
    xx(int u = 0,int v = 0,int w = 0,int nex = 0):
        u(u),v(v),w(w),nex(nex){}
}edge[maxn * 2];
int coun = 0;
///倍增算法
int dep[maxn];            ///深度
int fa[maxn][25];         /// fa[i][j] 表示 i 节点的第 2^j 的祖先
int n,m,q;        ///其中 m = log(n * 1.0) / log(2.0);  n 为总数,编号为 1 ~ n;


int update(int rt,int pos,int val){
    int newrt = tot ++,tmp = newrt;
    c[newrt] = c[rt] + val;
    int l = 1,r = en;
    while(l < r){
        int mid = (l + r) >> 1;
        if(pos <= mid){
            lson[newrt] = tot ++;
            rson[newrt] = rson[rt];
            newrt = lson[newrt];
            rt = lson[rt];
            r = mid;
        }
        else {
            rson[newrt] = tot ++;
            lson[newrt] = lson[rt];
            newrt = rson[newrt];
            rt = rson[rt];
            l = mid + 1;
        }
        c[newrt] = c[rt] + val;
    }
    return tmp;
}

void dfs(int f,int s){
    dep[s] = dep[f] + 1;
    for(int j = head[s];~j ;j = edge[j].nex){
        int v = edge[j].v;
        if(v != f){
            fa[v][0] = s;
            for(int i = 1;i <= m;i ++)
                fa[v][i] = fa[fa[v][i-1]][i-1];
            int pos = lower_bound(X + 1,X + 1 + en,edge[j].w) - X;
            T[v] = update(T[s],pos,1);
            dfs(s,v);
        }
    }
}

int lca(int u,int v){
    if(dep[u] < dep[v]) swap(u,v);      ///u 在下面
    int d = dep[u] - dep[v];
    for(int i = 0; (1 << i) <= d;i ++)      ///移动到相同深度
        if((1 << i) & d)                ///(1<<i)&f找到f化为2进制后1的位置,移动到相应的位置
            u = fa[u][i];               ///比如f=5(101),先移动2^0祖先,然后再移动2^2祖先

    if(u != v){
        for(int i = m;i >= 0;i --)
            if(fa[u][i] != fa[v][i])         ///从最大祖先开始,判断a,b祖先,是否相同
                u = fa[u][i], v = fa[v][i];  ///如不相同,a b同时向上移动2^j
        u = fa[u][0];
    }
    return u;
}

int build(int l,int r){
    int rt = tot ++;
    c[rt] = 0;
    if(l != r){
        int mid = (l + r) >> 1;
        lson[rt] = build(l,mid);
        rson[rt] = build(mid + 1,r);
    }
    return rt;
}

int query(int u,int v,int k){
    int l = 1,r = en;
    int lrt = T[u],rrt = T[v];
    int LCA = lca(u,v),LCArt = T[LCA];
    int faLCArt = T[fa[LCA][0]];
    int ans = 0;
    while(l < r){
        int mid = (l + r) >> 1;
        if(X[mid] <= k){   ///简单画一画就理解了
            ans += c[lson[rrt]] + c[lson[lrt]] - c[lson[LCArt]] * 2;
            l = mid + 1;
            rrt = rson[rrt]; lrt = rson[lrt];
            LCArt = rson[LCArt];
            faLCArt = rson[faLCArt];
        }
        else {
            r = mid;
            lrt = lson[lrt]; rrt = lson[rrt];
            LCArt = lson[LCArt];
            faLCArt = lson[faLCArt];
        }
    }
    if(l == r){
        if(k >= X[l])
            ans += c[rrt] + c[lrt] - c[lson[LCArt]] * 2;
    }
    return ans;
}

int main() {
    scanf("%d%d",&n,&q);
    m = log(n*1.0) / log(2.0);
    int cnt = 0;
    for(int i = 0;i < maxn;++ i) head[i] = -1;
    for(int i = 1;i < n;++ i){
        int u,v,w; scanf("%d%d%d",&u,&v,&w);
        edge[coun] = xx(u,v,w,head[u]);
        head[u] = coun ++;
        edge[coun] = xx(v,u,w,head[v]);
        head[v] = coun ++;
        X[++ cnt] = w;
    }

    sort(X + 1,X + 1 + cnt);
    en = unique(X + 1,X + 1 + cnt) - X - 1;
    T[0] = build(1,en);
    dep[0] = -1;
    dfs(0,1);
    while(q --){
        int u,v,k; scanf("%d%d%d",&u,&v,&k);
        printf("%d\n",query(u,v,k));
    }
    return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值